package Roguelike.Levels; import Roguelike.Ability.AbilityTree; import Roguelike.Ability.ActiveAbility.ActiveAbility; import Roguelike.AssetManager; import Roguelike.DungeonGeneration.DungeonFileParser.DFPRoom; import Roguelike.Entity.ActivationAction.ActivationActionGroup; import Roguelike.Entity.Entity; import Roguelike.Entity.EnvironmentEntity; import Roguelike.Entity.GameEntity; import Roguelike.Entity.Tasks.AbstractTask; import Roguelike.Entity.Tasks.TaskAttack; import Roguelike.Entity.Tasks.TaskMove; import Roguelike.Entity.Tasks.TaskWait; import Roguelike.Fields.Field; import Roguelike.Fields.Field.FieldLayer; import Roguelike.GameEvent.AdditionalSprite; import Roguelike.GameEvent.GameEventHandler; import Roguelike.Global; import Roguelike.Global.Direction; import Roguelike.Global.Passability; import Roguelike.Global.Statistic; import Roguelike.Items.Inventory; import Roguelike.Items.Item; import Roguelike.Items.TreasureGenerator; import Roguelike.Lights.Light; import Roguelike.Pathfinding.Pathfinder; import Roguelike.Pathfinding.ShadowCastCache; import Roguelike.Pathfinding.ShadowCaster; import Roguelike.RoguelikeGame; import Roguelike.RoguelikeGame.ScreenEnum; import Roguelike.Screens.GameScreen; import Roguelike.Sound.RepeatingSoundEffect; import Roguelike.Sound.SoundInstance; import Roguelike.Sprite.Sprite; import Roguelike.Sprite.SpriteAnimation.BumpAnimation; import Roguelike.Sprite.SpriteAnimation.MoveAnimation; import Roguelike.Sprite.SpriteAnimation.MoveAnimation.MoveEquation; import Roguelike.Sprite.SpriteEffect; import Roguelike.Tiles.GameTile; import Roguelike.Tiles.Point; import Roguelike.UI.Message; import Roguelike.Util.EnumBitflag; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import java.util.Iterator; public class Level { // ####################################################################// // region Constructor public Level( GameTile[][] grid ) { this.Grid = grid; this.width = grid.length; this.height = grid[0].length; } // endregion Constructor // ####################################################################// // region Lights public void calculateAmbient() { Color acol = new Color( Ambient ); acol.mul( acol.a ); acol.a = 1; for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { GameTile tile = Grid[x][y]; tile.ambientColour.set( acol ); } } // do shadows for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { GameTile tile = Grid[x][y]; if ( tile.tileData.shadow != null ) { //tile.tileData.shadow.apply( Grid, x, y ); } } } } private void calculateLight( float delta, Array<Light> lights ) { for ( Light l : lights ) { l.update( delta ); calculateSingleLight( l ); if ( l.copied ) { Global.LightPool.free( l ); } } } private void calculateSingleLight( Light l ) { Array<Point> output = l.shadowCastCache.getShadowCast( Grid, (int) l.lx, (int) l.ly, (int) l.baseIntensity + 1, null ); for ( Point tilePos : output ) { GameTile tile = getGameTile( tilePos ); if (!tile.visible) { continue; } float dst = 1 - Vector2.dst2( l.lx, l.ly, tile.x, tile.y ) / ( l.actualIntensity * l.actualIntensity ); if ( dst < 0 ) { dst = 0; } tempColour.set( l.colour ); tempColour.mul( tempColour.a ); tempColour.a = 1; tempColour.mul( dst ); tile.light.add( tempColour ); } } private void getLightsForTile( GameTile tile, Array<Light> output, int viewRange ) { int lx = tile.x; int ly = tile.y; int px = player.tile[0][0].x; int py = player.tile[0][0].y; if ( tile.lightObj != null ) { if ( checkLightCloseEnough( lx, ly, (int) tile.lightObj.baseIntensity, px, py, viewRange ) ) { tile.lightObj.lx = lx; tile.lightObj.ly = ly; output.add( tile.lightObj ); } } if ( tile.hasFieldLight ) { for ( FieldLayer layer : FieldLayer.values() ) { Field field = tile.fields.get( layer ); if ( field != null ) { Field.SpriteGroup group = field.getSpriteGroup(); if ( group.light != null && checkLightCloseEnough( lx, ly, (int) group.light.baseIntensity, px, py, viewRange ) ) { group.light.lx = lx; group.light.ly = ly; output.add( group.light ); } } } } if ( tile.environmentEntity != null && tile.environmentEntity.light != null && tile.environmentEntity.tile[0][0] == tile ) { if ( checkLightCloseEnough( lx, ly, (int) tile.environmentEntity.light.baseIntensity, px, py, viewRange ) ) { tile.environmentEntity.light.lx = lx; tile.environmentEntity.light.ly = ly; output.add( tile.environmentEntity.light ); } } if ( tile.spriteEffects.size > 0 ) { for ( SpriteEffect se : tile.spriteEffects ) { if ( se.light != null ) { if ( checkLightCloseEnough( lx, ly, (int) se.light.baseIntensity, px, py, viewRange ) ) { se.light.lx = lx; se.light.ly = ly; output.add( se.light ); } } } } if ( tile.entity != null && tile.entity.tile[0][0] == tile ) { tempLightList.clear(); tile.entity.getLight( tempLightList ); for ( Light l : tempLightList ) { if ( checkLightCloseEnough( lx, ly, (int) l.baseIntensity + 1, px, py, viewRange ) ) { l.lx = lx; l.ly = ly; if ( tile.entity.sprite.spriteAnimation != null ) { int[] offset = tile.entity.sprite.spriteAnimation.getRenderOffset(); l.lx += (float) offset[0] / (float) Global.TileSize; l.ly += (float) offset[1] / (float) Global.TileSize; } output.add( l ); } } } } private boolean checkLightCloseEnough( int lx, int ly, int intensity, int px, int py, int viewRange ) { return Math.max( Math.abs( px - lx ), Math.abs( py - ly ) ) <= viewRange + intensity; } // endregion Lights // ####################################################################// // region Cleanup private void cleanUpDeadForTile( GameTile tile ) { { GameEntity e = tile.entity; if ( e != null ) { if ( tile.visible ) { if ( e.canTakeDamage && e.hasDamage ) { e.pendingMessages.add( new Message( "-"+e.damageAccumulator, Color.RED ) ); e.damageAccumulator = 0; e.hasDamage = false; } if ( e.canTakeDamage && e.healingAccumulator > 0 ) { e.pendingMessages.add( new Message( "+"+e.healingAccumulator, Color.GREEN ) ); e.healingAccumulator = 0; } } else { e.damageAccumulator = 0; e.healingAccumulator = 0; } if ( e.canTakeDamage && e != player && e.HP <= 0 && !hasActiveEffects( e ) ) { int quality = Global.getQuality(); if ( e.quality > 1 ) { e.inventory.m_items.addAll( TreasureGenerator.generateLoot( quality + (e.quality-1), "random", MathUtils.random ) ); } else if ( e.essence > 0 && MathUtils.random( 4 ) == 0 ) { e.inventory.m_items.addAll( TreasureGenerator.generateLoot( quality, "random", MathUtils.random ) ); } entityDeathSound.play( e.tile[0][0] ); dropItems( e.getInventory(), e.tile[0][0], e.essence, e ); e.removeFromTile(); } else if ( e == player && e.HP <= 0 && !hasActiveEffects( e ) ) { GameScreen.Instance.displayGameOverMessage(); } if ( e.popupDuration <= 0 && e.popup != null && e.popup.length() == e.displayedPopup.length() && e.popupFade <= 0 ) { e.popupFade = 1; } } } { EnvironmentEntity e = tile.environmentEntity; if ( e != null ) { if ( e.canTakeDamage && e.damageAccumulator > 0 && tile.visible ) { e.pendingMessages.add( new Message( "-"+e.damageAccumulator, Color.RED ) ); e.damageAccumulator = 0; e.hasDamage = false; } if ( e.forceKill || ( e.canTakeDamage && e.HP <= 0 && !hasActiveEffects( e ) ) ) { dropItems( e.getInventory(), e.tile[0][0], e.essence, e ); for ( ActivationActionGroup group : e.onDeathActions) { if (group.enabled) { group.activate( e, null, 1 ); } } e.removeFromTile(); } if ( e.popupDuration <= 0 && e.popupFade <= 0 ) { e.popupFade = 1; } } } } private void dropItems( Inventory inventory, GameTile source, int essence, Object obj ) { Array<Point> possibleTiles = new Array<Point>(); for (Direction dir : Direction.values()) { if ( Global.CanMoveDiagonal || dir.isCardinal() ) { int nx = source.x + dir.getX(); int ny = source.y + dir.getY(); GameTile tile = getGameTile( nx, ny ); if ( tile != null && tile.getPassable( ItemDropPassability, obj ) ) { possibleTiles.add( Global.PointPool.obtain().set( nx, ny ) ); } } } float delay = 0; if (essence > 0) { delay = dropOrbs( essence, delay, GameTile.OrbType.EXPERIENCE, source, possibleTiles ); if ( MathUtils.random( 5 ) <= Global.LevelManager.hpDropCounter ) { Global.LevelManager.hpDropCounter -= 3; int amount = Math.max( 10, player.getMaxHP() / 4 ); delay += dropOrbs( amount, delay, GameTile.OrbType.HEALTH, source, possibleTiles ); } else { Global.LevelManager.hpDropCounter++; } if ( obj instanceof GameEntity && ((GameEntity)obj).quality > 1 ) { int amount = Math.max( 10, player.getMaxHP() / 3 ); delay += dropOrbs( amount, delay, GameTile.OrbType.HEALTH, source, possibleTiles ); } } for ( Item i : inventory.m_items ) { if ( i.canDrop && i.shouldDrop() ) { Point target = possibleTiles.size > 0 ? possibleTiles.random() : Global.PointPool.obtain().set( source ); GameTile tile = getGameTile( target ); if (tile.entity == player) { GameScreen.Instance.pickupQueue.add( i ); } else { tile.items.add( i ); } int[] diff = tile.getPosDiff( source ); MoveAnimation anim = new MoveAnimation( 0.2f, diff, MoveEquation.LEAP ); anim.leapHeight = 3; i.getIcon().spriteAnimation = anim; i.getIcon().renderDelay = delay; delay += 0.015f; } } Global.PointPool.freeAll( possibleTiles ); pickupOrbs(); } private float dropOrbs( int val, float delay, GameTile.OrbType type, GameTile source, Array<Point> possibleTiles) { if ( val > 0 ) { if ( possibleTiles.size > 0 ) { int blockSize = MathUtils.clamp( val / 10, 10, 100 ); while ( val > 0 ) { int block = Math.min( blockSize, val ); val -= block; Point target = possibleTiles.random(); GameTile tile = getGameTile( target ); int existingVal = tile.orbs.containsKey( type ) ? tile.orbs.get( type ) : 0; tile.orbs.put( type, existingVal + block ); int[] diff = tile.getPosDiff( source ); Sprite sprite = AssetManager.loadSprite( type.spriteName ); MoveAnimation anim = new MoveAnimation( 0.2f, diff, MoveEquation.LEAP ); anim.leapHeight = 3; sprite.spriteAnimation = anim; sprite.renderDelay = delay; delay += 0.02f; float scale = 0.5f + 0.5f * ( MathUtils.clamp( block, 10.0f, 1000.0f ) / 1000.0f ); sprite.baseScale[0] = scale; sprite.baseScale[1] = scale; tile.spriteEffects.add( new SpriteEffect( sprite, Direction.CENTER, null ) ); } } else { int existingVal = source.orbs.containsKey( type ) ? source.orbs.get( type ) : 0; source.orbs.put( type, existingVal + val ); int[] diff = new int[] { 0, 0 }; Sprite sprite = AssetManager.loadSprite( type.spriteName ); MoveAnimation anim = new MoveAnimation( 0.2f, diff, MoveEquation.LEAP ); anim.leapHeight = 3; sprite.spriteAnimation = anim; sprite.renderDelay = delay; delay += 0.02f; float scale = 0.5f + 0.5f * ( MathUtils.clamp( val, 10.0f, 1000.0f ) / 1000.0f ); sprite.baseScale[0] = scale; sprite.baseScale[1] = scale; source.spriteEffects.add( new SpriteEffect( sprite, Direction.CENTER, null ) ); } } return delay; } private void clearEffectsForTile( GameTile tile ) { if ( tile.spriteEffects.size > 0 ) { tile.spriteEffects.clear(); } if ( tile.environmentEntity != null) { tile.environmentEntity.pendingMessages.clear(); tile.environmentEntity.extraUIHP = 0; if (tile.environmentEntity.sprite != null) { tile.environmentEntity.sprite.spriteAnimation = null; } } if ( tile.entity != null ) { tile.entity.extraUIHP = 0; tile.entity.pendingMessages.clear(); if ( tile.entity.sprite != null ) { tile.entity.sprite.spriteAnimation = null; } } if ( tile.items.size > 0 ) { for ( Item i : tile.items ) { i.getIcon().spriteAnimation = null; } } } // endregion Cleanup // ####################################################################// // region Update // ---------------------------------------------------------------------- public void updateVisibleTiles() { if ( !isVisionRestricted ) { for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { Grid[x][y].visible = true; Grid[x][y].seen = true; Grid[x][y].seenBitflag.setAll( Direction.class ); Grid[x][y].unseenBitflag.setAll( Direction.class ); } } } else { for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { Grid[x][y].tempVisible = Grid[x][y].visible; Grid[x][y].visible = false; } } shadowCastStore.clear(); shadowCastStore.addAll( visibilityData.getCurrentShadowCast() ); Array<Point> output = visibilityData.getShadowCast( Grid, player.tile[0][0].x, player.tile[0][0].y, player.getVariable( Statistic.PERCEPTION ), player, true ); for ( Point tilePos : output ) { GameTile tile = getGameTile( tilePos ); if ( tile != null ) { tile.visible = true; if (!tile.seen) { tile.seen = true; updateUnseenBitflag( tilePos.x, tilePos.y ); } } } for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { GameTile tile = Grid[x][y]; if (tile.tempVisible != tile.visible) { updateSeenBitflag( x, y ); } } } } } // ---------------------------------------------------------------------- public void updateSeenBitflag(int x, int y) { for (Direction dir : Direction.values()) { GameTile tile = getGameTile( x + dir.getX(), y + dir.getY() ); if (tile != null) { buildTilingBitflag( tile.seenBitflag, tile.x, tile.y, SEENID ); } } } // ---------------------------------------------------------------------- public void updateUnseenBitflag(int x, int y) { for (Direction dir : Direction.values()) { GameTile tile = getGameTile( x + dir.getX(), y + dir.getY() ); if (tile != null) { buildTilingBitflag( tile.unseenBitflag, tile.x, tile.y, UNSEENID ); } } } // ---------------------------------------------------------------------- public void buildTilingBitflag(EnumBitflag<Direction> bitflag, int x, int y, long id) { // Build bitflag of surrounding tiles bitflag.clear(); for (Direction dir : Direction.values()) { GameTile otile = getGameTile( x + dir.getX(), y + dir.getY() ); if (otile != null) { // Attempt to find match boolean matchFound = false; if (otile.spriteGroup.tilingSprite != null && otile.spriteGroup.tilingSprite.thisID == id) { matchFound = true; } else if (otile.environmentEntity != null && otile.environmentEntity.tilingSprite != null && otile.environmentEntity.tilingSprite.thisID == id) { matchFound = true; } else if (!otile.seen && id == UNSEENID) { matchFound = true; } else if (!otile.visible && id == SEENID) { matchFound = true; } if (!matchFound) { if (otile.hasFields) { for (FieldLayer layer : FieldLayer.values()) { Field field = otile.fields.get( layer ); if (field != null) { Field.SpriteGroup group = field.getSpriteGroup(); if (group.tilingSprite != null && group.tilingSprite.thisID == id) { matchFound = true; break; } } } } } if (!matchFound) { bitflag.setBit( dir ); } } } } // ---------------------------------------------------------------------- private void updateStatusesForTile( GameTile tile ) { if ( tile.entity != null && tile.entity.tile[0][0] == tile ) { tile.entity.processStatuses(); } if ( tile.environmentEntity != null && tile.environmentEntity.tile[0][0] == tile ) { tile.environmentEntity.processStatuses(); } } // ---------------------------------------------------------------------- private void updatePopupsForTile( GameTile tile, float delta ) { if ( tile.entity != null && tile.entity.tile[0][0] == tile ) { if (tile.entity.dialogue != null) { if (tile.entity.dialogue.popupText != null) { tile.entity.setPopupText( tile.entity.dialogue.popupText, 2 ); tile.entity.dialogue.popupText = null; } if (tile.entity.dialogue.soundToBePlayed != null) { tile.entity.dialogue.soundToBePlayed.play( tile.entity.tile[0][0] ); tile.entity.dialogue.soundToBePlayed = null; } } if ( tile.entity.popup != null ) { if ( tile.entity.displayedPopup.length() < tile.entity.popup.length() ) { tile.entity.popupAccumulator += delta; while ( tile.entity.popupAccumulator >= 0 && tile.entity.displayedPopup.length() < tile.entity.popup.length() ) { tile.entity.popupAccumulator -= 0.02f; tile.entity.displayedPopup = tile.entity.popup.substring( 0, tile.entity.displayedPopup.length() + 1 ); } } } GameScreen.Instance.processMessageQueue( tile.entity, delta ); } if ( tile.environmentEntity != null && tile.environmentEntity.tile[0][0] == tile ) { if ( tile.environmentEntity.popup != null ) { if ( tile.environmentEntity.displayedPopup.length() < tile.environmentEntity.popup.length() ) { tile.environmentEntity.popupAccumulator += delta; while ( tile.environmentEntity.popupAccumulator >= 0 && tile.environmentEntity.displayedPopup.length() < tile.environmentEntity.popup.length() ) { tile.environmentEntity.popupAccumulator -= 0.02f; tile.environmentEntity.displayedPopup = tile.environmentEntity.popup.substring( 0, tile.environmentEntity.displayedPopup.length() + 1 ); } } } GameScreen.Instance.processMessageQueue( tile.environmentEntity, delta ); } } // ---------------------------------------------------------------------- private void updateExtraUIHPForTile( GameTile tile, float delta ) { if (tile.entity != null && tile.entity.extraUIHP > 0 && tile.entity.tile[0][0] == tile) { tile.entity.extraUIHPAccumulator += delta; while (tile.entity.extraUIHP > 0 && tile.entity.extraUIHPAccumulator > 0) { tile.entity.extraUIHP -= (float)(tile.entity.getMaxHP()) / 100.0f; if (tile.entity.extraUIHP < 0) { tile.entity.extraUIHP = 0; } float ratio = (float)tile.entity.extraUIHP / (float)(tile.entity.getMaxHP()); ratio = 0.02f - 0.02f * ratio; tile.entity.extraUIHPAccumulator -= ratio; } } if (tile.environmentEntity != null && tile.environmentEntity.extraUIHP > 0 && tile.environmentEntity.tile[0][0] == tile) { tile.environmentEntity.extraUIHPAccumulator += delta; while (tile.environmentEntity.extraUIHP > 0 && tile.environmentEntity.extraUIHPAccumulator > 0) { tile.environmentEntity.extraUIHP -= (float)(tile.environmentEntity.getMaxHP()) / 100.0f; if (tile.environmentEntity.extraUIHP < 0) { tile.environmentEntity.extraUIHP = 0; } float ratio = (float)tile.environmentEntity.extraUIHP / (float)(tile.environmentEntity.getMaxHP()); ratio = 0.02f - 0.02f * ratio; tile.environmentEntity.extraUIHPAccumulator -= ratio; } } } // ---------------------------------------------------------------------- private void updateSpriteEffectsForTile( GameTile tile, float delta ) { if ( tile.spriteEffects.size > 0 ) { Iterator<SpriteEffect> itr = tile.spriteEffects.iterator(); while ( itr.hasNext() ) { SpriteEffect e = itr.next(); boolean finished = e.Sprite.update( delta ); if ( finished ) { itr.remove(); } } } } // ---------------------------------------------------------------------- private void updateSpritesForTile( GameTile tile, float delta ) { for ( Sprite sprite : tile.getSprites() ) { sprite.update( delta ); } if ( tile.getTilingSprite() != null ) { //for (Sprite sprite : tile.tileData.tilingSprite.sprites) //{ // sprite.update( delta ); //} } if ( tile.hasFields ) { for ( FieldLayer layer : FieldLayer.values() ) { Field field = tile.fields.get( layer ); if ( field != null ) { Field.SpriteGroup group = field.getSpriteGroup(); if (group.sprite != null) { group.sprite.update( delta ); } } } } if ( tile.environmentEntity != null && tile.environmentEntity.tile[0][0] == tile && tile.environmentEntity.sprite != null ) { tile.environmentEntity.sprite.update( delta ); if (tile.environmentEntity.replacementSprite != null) { tile.environmentEntity.replacementSprite.update( delta ); } for (AdditionalSprite s : tile.environmentEntity.additionalSprites) { s.sprite.update( delta ); } } if ( tile.entity != null && tile.entity.tile[0][0] == tile && tile.entity.sprite != null ) { tile.entity.sprite.update( delta ); if (tile.entity.replacementSprite != null) { tile.entity.replacementSprite.update( delta ); } for (AdditionalSprite s : tile.entity.additionalSprites) { s.sprite.update( delta ); } } if ( tile.items.size > 0 ) { for ( Item i : tile.items ) { i.getIcon().update( delta ); } } } // endregion Update // ####################################################################// // region Process private void processPlayer() { player.updatedAbilityDam = false; player.updatedAbilityHeal = false; AbstractTask task = player.tasks.removeIndex( 0 ); for ( GameEventHandler handler : player.getAllHandlers() ) { handler.onTask( player, task ); } if ( !task.cancel ) { task.processTask( player ); } if (task instanceof TaskMove) { for (AbilityTree ab : player.slottedAbilities) { if (ab != null) { ab.current.current.onMove(); } } } else if (task instanceof TaskAttack) { for (AbilityTree ab : player.slottedAbilities) { if (ab != null) { ab.current.current.onAttack(); } } } else if (task instanceof TaskWait) { for (AbilityTree ab : player.slottedAbilities) { if (ab != null) { ab.current.current.onWait(); } } } if ( !(task instanceof TaskMove ) ) { for (int x = 0; x < player.size; x++) { for (int y = 0; y < player.size; y++) { player.tile[x][y].prevEntity = player; } } } float actionCost = task.cost * player.getActionDelay(); Global.AUT += actionCost; Global.DayNightFactor = (float) ( 0.1f + ( ( ( Math.sin( Global.AUT / 100.0f ) + 1.0f ) / 2.0f ) * 0.9f ) ); player.update( actionCost ); // Advance all entity accumulators and build list getAllEntitiesToBeProcessed( actionCost ); tempEnvironmentEntityList.clear(); getAllEnvironmentEntitiesToBeProcessed( tempEnvironmentEntityList ); for ( EnvironmentEntity ee : tempEnvironmentEntityList ) { ee.update( actionCost ); for (int i = 0; i < ee.proximityActions.size; i++) { ActivationActionGroup action = ee.proximityActions.get(i); if ( action.enabled && action.checkCondition( ee, player, actionCost )) { action.activate( ee, player, actionCost ); } } } tempFieldList.clear(); getAllFields( tempFieldList ); for ( Field f : tempFieldList ) { if ( f.tile == null ) { continue; } f.update( actionCost ); if ( f.stacks < 1 ) { f.tile.fields.put( f.layer, null ); f.tile = null; } } pickupOrbs(); if ( task instanceof TaskMove && player.tile[0][0].items.size > 0 ) { for ( Item item : player.tile[0][0].items ) { GameScreen.Instance.pickupQueue.add( item ); } pickupItemSound.play( player.tile[ 0 ][ 0 ] ); player.tile[0][0].items.clear(); } player.tile[0][0].processFieldEffectsForEntity( player, actionCost ); // check if enemy visible if ( enemyVisible() ) { // Clear pending moves player.AI.setData( "Pos", null ); player.AI.setData( "Rest", null ); } if ( player.sprite.spriteAnimation instanceof BumpAnimation ) { player.AI.setData( "Pos", null ); player.AI.setData( "Rest", null ); } } public void pickupOrbs() { if ( player.tile[ 0 ][ 0 ].orbs.size > 0 ) { for ( GameTile.OrbType type : GameTile.OrbType.values() ) { if ( player.tile[ 0 ][ 0 ].orbs.containsKey( type ) ) { int val = player.tile[ 0 ][ 0 ].orbs.get( type ); if ( type == GameTile.OrbType.EXPERIENCE ) { player.pendingMessages.add( new Message( "+" + val + " xp", Color.YELLOW ) ); for (AbilityTree tree : player.slottedAbilities) { if (tree != null) { tree.current.gainExp( val ); } } pickupXPSound.play( player.tile[ 0 ][ 0 ] ); player.tile[ 0 ][ 0 ].orbs.remove( type ); } else if ( type == GameTile.OrbType.HEALTH ) { int healthMissing = player.getMaxHP() - player.HP; int restored = Math.min( healthMissing, val ); if (restored > 0) { player.applyHealing( restored ); pickupHPSound.play( player.tile[ 0 ][ 0 ] ); player.tile[ 0 ][ 0 ].orbs.remove( type ); if (val - restored > 0) { player.tile[ 0 ][ 0 ].orbs.put( GameTile.OrbType.HEALTH, val - restored ); } } } } } } } public boolean isInTurn() { return turnCount >= 0 && turnCount < 100; } public boolean canStartTurn() { return !hasActiveEffects() && Global.CurrentDialogue == null && updateAccumulator >= updateDeltaStep && player.AI != null; } public void startTurn() { turnCount = 0; } public void doTurnWork() { if (turnCount == -1) { turnCount = 0; } else if (turnCount == 0) { player.AI.update( player ); if ( player.tasks.size > 0 ) { turnCount = 1; } } else if (turnCount == 1) { processPlayer(); player.updateShadowCast(); turnCount = 2; } else if (turnCount == 2) { if ( toBeProcessedList.size > 0 ) { GameEntity e = toBeProcessedList.get( 0 ); boolean complete = processEntityTurn( e ); if (complete) { toBeProcessedList.removeIndex( 0 ); } } if ( ActiveAbilities.size > 0 ) { Iterator<ActiveAbility> itr = ActiveAbilities.iterator(); while ( itr.hasNext() ) { ActiveAbility aa = itr.next(); boolean finished = aa.update(); if ( finished ) { itr.remove(); } } } if ( NewActiveAbilities.size > 0 ) { ActiveAbilities.addAll( NewActiveAbilities, 0, NewActiveAbilities.size ); NewActiveAbilities.clear(); } if (toBeProcessedList.size == 0 && !hasAbilitiesToUpdate()) { if (player.tasks.size > 0) { turnCount = 1; } else { turnCount = 3; } } } else if (turnCount == 3) { saveCounter++; if (saveCounter == 10) { Global.save(); saveCounter = 0; } turnCount = 100; } } private boolean processEntityTurn( GameEntity e ) { if ( e.HP <= 0 ) { return true; } e.updateShadowCast(); // If entity can take action if ( e.actionDelayAccumulator > 0 ) { // If no tasks queued, process the ai if ( e.tasks.size == 0 ) { e.AI.setData( "SpawnPos", e.spawnPos ); e.AI.update( e ); } // If a task is queued, process it if ( e.tasks.size > 0 ) { e.updatedAbilityDam = false; e.updatedAbilityHeal = false; AbstractTask task = e.tasks.removeIndex( 0 ); for ( GameEventHandler handler : e.getAllHandlers() ) { handler.onTask( e, task ); } if ( !task.cancel ) { task.processTask( e ); } if (task instanceof TaskMove) { for (AbilityTree ab : e.slottedAbilities) { if (ab != null) { ab.current.current.onMove(); } } } else if (task instanceof TaskAttack) { for (AbilityTree ab : e.slottedAbilities) { if (ab != null) { ab.current.current.onAttack(); } } } else if (task instanceof TaskWait) { for (AbilityTree ab : e.slottedAbilities) { if (ab != null) { ab.current.current.onWait(); } } } if ( !(task instanceof TaskMove ) ) { for (int x = 0; x < e.size; x++) { for (int y = 0; y < e.size; y++) { e.tile[x][y].prevEntity = e; } } } float actionCost = task.cost * e.getActionDelay(); e.actionDelayAccumulator -= actionCost * e.getActionDelay(); for ( EnvironmentEntity ee : tempEnvironmentEntityList ) { for (int i = 0; i < ee.proximityActions.size; i++) { ActivationActionGroup action = ee.proximityActions.get(i); if ( action.enabled && action.checkCondition( ee, e, actionCost )) { action.activate( ee, e, actionCost ); } } } e.tile[0][0].processFieldEffectsForEntity( e, actionCost ); } else { e.actionDelayAccumulator -= e.getActionDelay(); } } return e.actionDelayAccumulator <= 0; } // endregion Process // ####################################################################// // region Getters private void getAllEntitiesToBeProcessed( float cost ) { for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { GameEntity e = Grid[x][y].entity; if ( e != null && e != player && e.tile[0][0] == Grid[x][y] ) { if ( e.tile[0][0].visible ) { e.seen = true; } if ( !e.seen ) { continue; } if ( Math.min( Math.abs( x - player.tile[0][0].x ), Math.abs( y - player.tile[0][0].y ) ) > 25 ) { continue; } e.update( cost ); if ( e.actionDelayAccumulator > 0 || e.tasks.size > 0 ) { toBeProcessedList.add( e ); } } } } for ( ActiveAbility aa : ActiveAbilities ) { aa.updateAccumulators( cost ); } } public final GameTile[][] getGrid() { return Grid; } public final GameTile getGameTile( Point pos ) { return getGameTile( pos.x, pos.y ); } public final Entity getEntityWithUID( String UID ) { for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { GameTile tile = getGameTile( x, y ); if ( tile.environmentEntity != null ) { if ( tile.environmentEntity.UID.equals( UID ) ) { return tile.environmentEntity; } } if ( tile.entity != null ) { if ( tile.entity.UID.equals( UID ) ) { return tile.entity; } } } } return null; } public final GameTile getGameTile( int x, int y ) { if ( x < 0 || x >= width || y < 0 || y >= height ) { return null; } return Grid[ x ][ y ]; } public final void getAllFields( Array<Field> list ) { for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { if ( Grid[x][y].hasFields ) { for ( FieldLayer layer : FieldLayer.values() ) { Field field = Grid[x][y].fields.get( layer ); if ( field != null ) { list.add( field ); } } } } } } public final void getAllEntities( Array<GameEntity> list ) { for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { if ( Grid[x][y].entity != null && Grid[x][y].entity.tile[0][0] == Grid[x][y] ) { list.add( Grid[x][y].entity ); } } } } public final void getAllEnvironmentEntities( Array<EnvironmentEntity> list ) { for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { if ( Grid[x][y].environmentEntity != null && Grid[x][y].environmentEntity.tile[0][0] == Grid[x][y] ) { list.add( Grid[x][y].environmentEntity ); } } } } public final void getAllEnvironmentEntitiesToBeProcessed( Array<EnvironmentEntity> list ) { for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { if ( Grid[x][y].environmentEntity != null && Grid[x][y].environmentEntity.tile[0][0] == Grid[x][y] ) { if ( Math.min( Math.abs( x - player.tile[0][0].x ), Math.abs( y - player.tile[0][0].y ) ) > 25 ) { continue; } list.add( Grid[x][y].environmentEntity ); } } } } private boolean hasAbilitiesToUpdate() { if ( ActiveAbilities.size > 0 ) { for ( ActiveAbility aa : ActiveAbilities ) { if ( aa.needsUpdate() ) { return true; } } } return false; } private boolean enemyVisible() { boolean enemy = false; for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { if ( Grid[x][y].visible && Grid[x][y].entity != null ) { if ( !Grid[x][y].entity.isAllies( player ) ) { enemy = true; break; } } } if ( enemy ) { break; } } return enemy; } private boolean hasActiveEffects() { boolean activeEffects = false; for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { if ( !Grid[x][y].visible ) { continue; } if ( Grid[x][y].spriteEffects.size > 0 ) { activeEffects = true; break; } { Entity e = Grid[x][y].entity; if ( e != null ) { boolean active = hasActiveEffects( e ); if ( active ) { activeEffects = true; break; } } } { Entity e = Grid[x][y].environmentEntity; if ( e != null ) { boolean active = hasActiveEffects( e ); if ( active ) { activeEffects = true; break; } } } } if ( activeEffects ) { break; } } return activeEffects; } private boolean hasActiveEffects( Entity e ) { if ( e.extraUIHP > 0 ) { return true; } if ( e.tile[0][0].spriteEffects.size > 0 ) { return true; } boolean activeEffects = false; if ( e.sprite != null && e.sprite.spriteAnimation != null ) { activeEffects = true; } return activeEffects; } // endregion Getters // ####################################################################// // region Misc public void addActiveAbility( ActiveAbility aa ) { NewActiveAbilities.add( aa ); } public void playEntitySound() { Array<GameEntity> validEntities = new Array<GameEntity>( ); Array<GameEntity> temp = new Array<GameEntity>( ); getAllEntities( temp ); for (GameEntity ge : temp) { if (ge.HP > 0 && ge.sprite.sound != null) { if (Vector2.dst2( ge.tile[0][0].x, ge.tile[0][0].y, player.tile[0][0].x, player.tile[0][0].y ) < 100) { validEntities.add( ge ); } } } if (validEntities.size > 0) { GameEntity chosen = validEntities.random(); chosen.sprite.sound.play( chosen.tile[0][0] ); } } public void advance( float delta ) { updateAccumulator += delta; enemySoundAccumulator += delta; if (enemySoundAccumulator > 10) { playEntitySound(); enemySoundAccumulator -= 10 + MathUtils.random() * 10; } player.updateShadowCast(); updateVisibleTiles(); lightList.clear(); int playerViewRange = player.getVariable( Statistic.PERCEPTION ); for ( int x = 0; x < width; x++ ) { for ( int y = 0; y < height; y++ ) { GameTile tile = Grid[x][y]; if ( tile.visible ) { updateSpritesForTile( tile, delta ); updateSpriteEffectsForTile( tile, delta ); updatePopupsForTile( tile, delta ); updateExtraUIHPForTile( tile, delta ); updateStatusesForTile( tile ); } else { clearEffectsForTile( tile ); } cleanUpDeadForTile( tile ); tile.light.set( tile.ambientColour ); if ( affectedByDayNight ) { tile.light.mul( Global.DayNightFactor ); tile.light.a = 1; } getLightsForTile( tile, lightList, playerViewRange ); } } if ( ActiveAbilities.size > 0 ) { for ( ActiveAbility aa : ActiveAbilities ) { aa.getSprite().update( delta ); if ( aa.light != null ) { for ( GameTile t : aa.AffectedTiles ) { int lx = t.x; int ly = t.y; if ( checkLightCloseEnough( lx, ly, (int) aa.light.baseIntensity, player.tile[0][0].x, player.tile[0][0].y, playerViewRange ) ) { Light light = aa.light.copy(); light.lx = lx; light.ly = ly; light.copied = true; lightList.add( light ); } } } } } for ( AbilityTree a : player.slottedAbilities ) { if ( a != null && a.current.current instanceof ActiveAbility ) { ActiveAbility aa = (ActiveAbility) a.current.current; aa.source = player.tile[0][0]; aa.hasValidTargets = aa.getValidTargets().size > 0; } } calculateLight( delta, lightList ); for ( RepeatingSoundEffect sound : ambientSounds ) { sound.update( delta ); } } // endregion Misc // ####################################################################// // region Data public static final long SEENID = "seen".hashCode(); public static final long UNSEENID = "unseen".hashCode(); private int saveCounter = 0; private int turnCount = -1; private Array<ActiveAbility> NewActiveAbilities = new Array<ActiveAbility>( false, 16 ); public boolean affectedByDayNight = false; private Array<GameEntity> toBeProcessedList = new Array<GameEntity>( false, 16 ); private Array<Light> lightList = new Array<Light>( false, 16 ); private Array<EnvironmentEntity> tempEnvironmentEntityList = new Array<EnvironmentEntity>( false, 16 ); private Array<Field> tempFieldList = new Array<Field>( false, 16 ); private Array<Light> tempLightList = new Array<Light>( false, 16 ); private float updateDeltaStep = 0.05f; private float updateAccumulator; private Array<Point> shadowCastStore = new Array<Point>( ); private static final EnumBitflag<Passability> ItemDropPassability = new EnumBitflag<Passability>( Passability.WALK, Passability.ENTITY ); public final ShadowCastCache visibilityData = new ShadowCastCache(); private final Color tempColour = new Color(); public Array<ActiveAbility> ActiveAbilities = new Array<ActiveAbility>( false, 16 ); public Array<RepeatingSoundEffect> ambientSounds = new Array<RepeatingSoundEffect>(); public String bgmName; public String fileName; public int depth; public long seed; public Array<DFPRoom> requiredRooms; public String UID; public GameEntity player; public boolean inTurn = false; public boolean isVisionRestricted = true; public Color Ambient = new Color( 0.1f, 0.1f, 0.3f, 1.0f ); public Sprite background; public GameTile[][] Grid; public int width; public int height; public float enemySoundAccumulator; public static final SoundInstance pickupXPSound = SoundInstance.getSound( "PickupXP" ); public static final SoundInstance pickupHPSound = SoundInstance.getSound( "PickupHP" ); public static final SoundInstance pickupItemSound = SoundInstance.getSound( "PickupItem" ); public static final SoundInstance entityDeathSound = SoundInstance.getSound( "EntityDeath" ); // endregion Data // ####################################################################// }