diff --git a/source/core/assets/configs/tower.json b/source/core/assets/configs/tower.json index c7a95392e..df72c561c 100644 --- a/source/core/assets/configs/tower.json +++ b/source/core/assets/configs/tower.json @@ -33,6 +33,5 @@ "health": 10, "baseAttack": 10, "cost": 10 - } } \ No newline at end of file diff --git a/source/core/assets/images/towers/fire_tower_atlas.atlas b/source/core/assets/images/towers/fire_tower_atlas.atlas index 7c9ce2206..4c14d5e15 100644 --- a/source/core/assets/images/towers/fire_tower_atlas.atlas +++ b/source/core/assets/images/towers/fire_tower_atlas.atlas @@ -1,83 +1,167 @@ fire_tower_atlas.png -size: 1024, 64 +size: 2048, 64 format: RGBA8888 filter: Nearest, Nearest repeat: none attack rotate: false - xy: 122, 2 + xy: 242, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 1 attack rotate: false - xy: 302, 2 + xy: 542, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 3 attack rotate: false - xy: 422, 2 + xy: 782, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 0 attack rotate: false - xy: 602, 2 + xy: 1082, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 2 -idle +death rotate: false xy: 62, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 - index: 1 -idle + index: 6 +death rotate: false xy: 182, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 3 -idle +death + rotate: false + xy: 302, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 8 +death rotate: false xy: 362, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 0 -idle +prepAttack rotate: false - xy: 542, 2 + xy: 362, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 1 +death + rotate: false + xy: 482, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 5 +death + rotate: false + xy: 662, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 11 +death + rotate: false + xy: 722, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 2 -prep_attack +death rotate: false - xy: 2, 2 + xy: 842, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 7 +death + rotate: false + xy: 1022, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 4 +death + rotate: false + xy: 1142, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 9 +death + rotate: false + xy: 1202, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 10 +death + rotate: false + xy: 1262, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 1 +idle + rotate: false + xy: 122, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 1 -prep_attack +idle rotate: false - xy: 242, 2 + xy: 422, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 3 +idle + rotate: false + xy: 602, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 0 -prep_attack +idle rotate: false - xy: 482, 2 + xy: 902, 2 size: 58, 58 orig: 58, 58 offset: 0, 0 index: 2 +prepAttack + rotate: false + xy: 2, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 2 +prepAttack + rotate: false + xy: 962, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 0 diff --git a/source/core/assets/images/towers/fire_tower_atlas.png b/source/core/assets/images/towers/fire_tower_atlas.png index a8c5cc3ee..cae7db217 100644 Binary files a/source/core/assets/images/towers/fire_tower_atlas.png and b/source/core/assets/images/towers/fire_tower_atlas.png differ diff --git a/source/core/assets/images/towers/stun_tower.atlas b/source/core/assets/images/towers/stun_tower.atlas index f0033a197..4cae61f81 100644 --- a/source/core/assets/images/towers/stun_tower.atlas +++ b/source/core/assets/images/towers/stun_tower.atlas @@ -1,118 +1,174 @@ stun_tower.png -size: 1024, 64 +size: 256, 256 format: RGBA8888 filter: Nearest, Nearest repeat: none attack rotate: false - xy: 2, 2 + xy: 59, 207 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 4 attack rotate: false - xy: 116, 2 + xy: 173, 207 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 9 attack rotate: false - xy: 173, 2 + xy: 2, 160 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 1 attack rotate: false - xy: 230, 2 + xy: 2, 113 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 6 attack rotate: false - xy: 344, 2 + xy: 2, 19 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 3 attack rotate: false - xy: 458, 2 + xy: 116, 160 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 8 attack rotate: false - xy: 572, 2 + xy: 59, 113 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 0 attack rotate: false - xy: 629, 2 + xy: 59, 66 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 5 attack rotate: false - xy: 743, 2 + xy: 116, 113 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 2 attack rotate: false - xy: 800, 2 + xy: 173, 113 size: 55, 45 orig: 55, 45 offset: 0, 0 index: 7 +death + rotate: false + xy: 116, 76 + size: 40, 35 + orig: 40, 35 + offset: 0, 0 + index: 6 +death + rotate: false + xy: 116, 39 + size: 40, 35 + orig: 40, 35 + offset: 0, 0 + index: 3 +death + rotate: false + xy: 116, 2 + size: 40, 35 + orig: 40, 35 + offset: 0, 0 + index: 0 +death + rotate: false + xy: 158, 76 + size: 40, 35 + orig: 40, 35 + offset: 0, 0 + index: 5 +death + rotate: false + xy: 200, 76 + size: 40, 35 + orig: 40, 35 + offset: 0, 0 + index: 2 +death + rotate: false + xy: 158, 39 + size: 40, 35 + orig: 40, 35 + offset: 0, 0 + index: 7 +death + rotate: false + xy: 158, 2 + size: 40, 35 + orig: 40, 35 + offset: 0, 0 + index: 4 +death + rotate: false + xy: 200, 39 + size: 40, 35 + orig: 40, 35 + offset: 0, 0 + index: 1 idle rotate: false - xy: 59, 2 + xy: 2, 207 size: 55, 45 orig: 55, 45 offset: 0, 0 - index: 1 + index: 4 idle rotate: false - xy: 287, 2 + xy: 116, 207 size: 55, 45 orig: 55, 45 offset: 0, 0 - index: 3 + index: 1 idle rotate: false - xy: 401, 2 + xy: 2, 66 size: 55, 45 orig: 55, 45 offset: 0, 0 - index: 0 + index: 3 idle rotate: false - xy: 515, 2 + xy: 59, 160 size: 55, 45 orig: 55, 45 offset: 0, 0 - index: 5 + index: 0 idle rotate: false - xy: 686, 2 + xy: 173, 160 size: 55, 45 orig: 55, 45 offset: 0, 0 - index: 2 + index: 5 idle rotate: false - xy: 857, 2 + xy: 59, 19 size: 55, 45 orig: 55, 45 offset: 0, 0 - index: 4 + index: 2 diff --git a/source/core/assets/images/towers/stun_tower.png b/source/core/assets/images/towers/stun_tower.png index 025b35a4c..f88d66a1d 100644 Binary files a/source/core/assets/images/towers/stun_tower.png and b/source/core/assets/images/towers/stun_tower.png differ diff --git a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java index 0c3c3e46a..8409382ba 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -7,6 +7,8 @@ import com.csse3200.game.components.ProjectileEffects; import com.csse3200.game.areas.terrain.TerrainFactory; import com.csse3200.game.areas.terrain.TerrainFactory.TerrainType; +import com.csse3200.game.components.TouchAttackComponent; +import com.csse3200.game.components.player.PlayerStatsDisplay; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.*; import com.csse3200.game.physics.PhysicsLayer; @@ -31,7 +33,11 @@ public class ForestGameArea extends GameArea { private static final int NUM_BUILDINGS = 4; private static final int NUM_GHOSTS = 0; private static final int NUM_GRUNTS = 5; - private static final int NUM_BOSS=4; + private static final int NUM_BOSS = 4; + + + private Timer bossSpawnTimer; + private int bossSpawnInterval = 10000; // 1 minute in milliseconds private static final int NUM_WEAPON_TOWERS = 3; private static final GridPoint2 PLAYER_SPAWN = new GridPoint2(0, 0); // Temporary spawn point for testing @@ -39,7 +45,7 @@ public class ForestGameArea extends GameArea { // Required to load assets before using them private static final String[] forestTextures = { - "images/ingamebg.png", + "images/ingamebg.png", "images/box_boy_leaf.png", "images/background/building1.png", "images/ghost_1.png", @@ -105,6 +111,8 @@ public class ForestGameArea extends GameArea { "images/mobs/xenoGrunt.atlas", "images/towers/fire_tower_atlas.atlas", "images/towers/stun_tower.atlas", + "images/mobs/xenoGruntRunning.atlas", + "images/xenoGrunt.atlas", "images/mobs/robot.atlas", "images/mobs/rangeBossRight.atlas", "images/towers/DroidTower.atlas", @@ -138,8 +146,8 @@ public class ForestGameArea extends GameArea { private final TerrainFactory terrainFactory; private Entity player; - - // Variables to be used with spawn projectile methods. This is the variable + + // Variables to be used with spawn projectile methods. This is the variable // that should occupy the direction param. private static final int towardsMobs = 100; private Entity bossKing2; @@ -147,6 +155,7 @@ public class ForestGameArea extends GameArea { /** * Initialise this ForestGameArea to use the provided TerrainFactory. + * * @param terrainFactory TerrainFactory used to create the terrain for the GameArea. * @requires terrainFactory != null */ @@ -155,39 +164,45 @@ public ForestGameArea(TerrainFactory terrainFactory) { this.terrainFactory = terrainFactory; } - /** Create the game area, including terrain, static entities (trees), dynamic entities (player) */ + /** + * Create the game area, including terrain, static entities (trees), dynamic entities (player) + */ @Override public void create() { // Load game assets loadAssets(); displayUI(); spawnTerrain(); +// spawnBuilding1(); +// spawnBuilding2(); +// spawnMountains(); // Set up infrastructure for end game tracking - player = spawnPlayer(); player.getEvents().addListener("spawnWave", this::spawnXenoGrunts); playMusic(); // Types of projectile - +// spawnAoeProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f), 1); + spawnProjectile(new Vector2(0, 10), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); + spawnMultiProjectile(new Vector2(0, 10), PhysicsLayer.NPC, towardsMobs, 20, new Vector2(2f, 2f), 7); + spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.HUMANS, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); spawnPierceFireBall(new Vector2(2, 3), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); spawnRicochetFireball(new Vector2(2, 4), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); spawnSplitFireWorksFireBall(new Vector2(2, 5), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f), 12); spawnEffectProjectile(new Vector2(2, 6), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.SLOW, false); // spawnProjectileTest(new Vector2(0, 8), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); - - spawnXenoGrunts(); - spawnScrap(); - spawnIncome(); - spawnGhosts(); +// spawnGhosts(); spawnWeaponTower(); +// spawnIncome(); + spawnScrap(); spawnTNTTower(); - spawnDroidTower(); - spawnGapScanners(); - spawnIncome(); +// spawnDroidTower(); +// spawnGapScanners(); +// bossKing1 = spawnBossKing1(); +// bossKing2 = spawnBossKing2(); bossKing2 = spawnBossKing2(); } @@ -217,7 +232,7 @@ private void spawnTerrain() { // Right spawnEntityAt( ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), - new GridPoint2(tileBounds.x , 0), + new GridPoint2(tileBounds.x, 0), false, false); // Top @@ -233,6 +248,46 @@ private void spawnTerrain() { spawnEntityAt( ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH * 7), new GridPoint2(0, 0), false, false); } +// private void spawnBuilding1() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); +// +// for (int i = 0; i < NUM_BUILDINGS; i++) { +// GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); +// Entity building1 = ObstacleFactory.createBuilding1(); +// spawnEntityAt(building1, randomPos, true, false); +// } +// } +// private void spawnBuilding2() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); +// +// for (int i = 0; i < NUM_BUILDINGS; i++) { +// GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); +// Entity building2 = ObstacleFactory.createBuilding2(); +// spawnEntityAt(building2, randomPos, true, false); +// } +// } + + +// private void spawnMountains() { +// ArrayList<GridPoint2> fixedPositions = new ArrayList<>(); //Generating ArrayList +// +// fixedPositions.add(new GridPoint2(5, 8)); +// fixedPositions.add(new GridPoint2(12, 4)); +// fixedPositions.add(new GridPoint2(20, 10)); +// fixedPositions.add(new GridPoint2(33, 17)); +// +// for (GridPoint2 fixedPos : fixedPositions) { +// Entity tree = ObstacleFactory.createMountain(); +// spawnEntityAt(tree, fixedPos, true, false); +// } +// for (int i = 0; i < NUM_BUILDINGS; i++) { +// GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); +// Entity building1 = ObstacleFactory.createBuilding1(); +// spawnEntityAt(building1, randomPos, true, false); +// } +// } private void spawnBuilding2() { GridPoint2 minPos = new GridPoint2(0, 0); @@ -245,9 +300,11 @@ private void spawnBuilding2() { } } + private Entity spawnPlayer() { Entity newPlayer = PlayerFactory.createPlayer(); spawnEntityAt(newPlayer, PLAYER_SPAWN, true, true); + newPlayer.addComponent(new TouchAttackComponent(PhysicsLayer.NPC)); return newPlayer; } @@ -258,51 +315,50 @@ private Entity spawnPlayer(GridPoint2 position) { return newPlayer; } - private void spawnGhosts() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(0, 2); - - for (int i = 0; i < NUM_GHOSTS; i++) { - int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate - int randomY = MathUtils.random(0, maxPos.y); - GridPoint2 randomPos = new GridPoint2(fixedX, randomY); - Entity ghost = createGhost(player); - spawnEntityAt(ghost, randomPos, true, true); - } - } - - private Entity spawnBossKing1() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); - GridPoint2 randomPos - = new GridPoint2(0, 0); - Entity ghostKing = NPCFactory.createGhostKing(player); - spawnEntityAt(ghostKing, randomPos, true, true); - return ghostKing; - } +// private void spawnGhosts() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(0, 2); +// +// for (int i = 0; i < NUM_GHOSTS; i++) { +// int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate +// int randomY = MathUtils.random(0, maxPos.y); +// GridPoint2 randomPos = new GridPoint2(fixedX, randomY); +// Entity ghost = createGhost(player); +// spawnEntityAt(ghost, randomPos, true, true); +// } +// } + +// private Entity spawnBossKing1() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); +// GridPoint2 randomPos +// = new GridPoint2(0, 0); +// Entity ghostKing = NPCFactory.createGhostKing(player); +// spawnEntityAt(ghostKing, randomPos, true, true); +// return ghostKing; +// } - /** - * Spawns a projectile that only heads towards the enemies in its lane. - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - * + /** + * Spawns a projectile that only heads towards the enemies in its lane. + * + * @param position The position of the Entity that's shooting the projectile. + * @param targetLayer The enemy layer of the "shooter". + * @param direction The direction the projectile should head towards. + * @param speed The speed of the projectiles. */ private void spawnProjectile(Vector2 position, short targetLayer, int direction, Vector2 speed) { Entity Projectile = ProjectileFactory.createFireBall(targetLayer, new Vector2(direction, position.y), speed); Projectile.setPosition(position); spawnEntity(Projectile); } - /** + + /** * Spawns a projectile specifically for general mobs/xenohunters - * - * @param position The position of the Entity that's shooting the projectile. + * + * @param position The position of the Entity that's shooting the projectile. * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - * + * @param direction The direction the projectile should head towards. + * @param speed The speed of the projectiles. */ private void spawnProjectileTest(Vector2 position, short targetLayer, int direction, Vector2 speed) { Entity Projectile = ProjectileFactory.createEngineerBullet(targetLayer, new Vector2(direction, position.y), speed); @@ -311,21 +367,37 @@ private void spawnProjectileTest(Vector2 position, short targetLayer, int direct } /** - * Spawns a projectile to be used for multiple projectile function. - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param space The space between the projectiles' destination. - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - * + * Spawns a projectile to be used for multiple projectile function. + * + * @param position The position of the Entity that's shooting the projectile. + * @param targetLayer The enemy layer of the "shooter". + * @param space The space between the projectiles' destination. + * @param direction The direction the projectile should head towards. + * @param speed The speed of the projectiles. */ - private void spawnProjectile(Vector2 position, short targetLayer, int space, int direction, Vector2 speed) { + private void spawnProjectile(Vector2 position, short targetLayer, int space, int direction, Vector2 speed) { Entity Projectile = ProjectileFactory.createFireBall(targetLayer, new Vector2(direction, position.y + space), speed); Projectile.setPosition(position); spawnEntity(Projectile); } + +// private Entity spawnBossKing() { +// for (int i = 0; i < NUM_BOSS; i++) { +// int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate +// int randomY = MathUtils.random(0, maxPos.y); +// GridPoint2 randomPos = new GridPoint2(fixedX, randomY); +// bossKing1 = BossKingFactory.createBossKing1(player); +// spawnEntityAt(bossKing1, +// randomPos, +// true, +// false); +// } +// return bossKing1; +// +// } + + private void spawnXenoGrunts() { int[] pickedLanes = new Random().ints(1, 7) .distinct().limit(5).toArray(); @@ -338,6 +410,35 @@ private void spawnXenoGrunts() { } } +// private Entity spawnGhostKing() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(0, 0); +// GridPoint2 randomPos +// = RandomUtils.random(minPos, maxPos); +// // = new GridPoint2(26, 26); +// Entity ghostKing = NPCFactory.createGhostKing(player); +// spawnEntityAt(ghostKing, randomPos, true, true); +// return ghostKing; +// +// } + +// private Entity spawnBossKing2() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); +// +// for (int i = 0; i < NUM_BOSS; i++) { +// int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate +// int randomY = MathUtils.random(0, maxPos.y); +// GridPoint2 randomPos = new GridPoint2(fixedX, randomY); +// bossKing2 = BossKingFactory.createBossKing2(player); +// spawnEntityAt(bossKing2, +// randomPos, +// true, +// false); +// } +// return bossKing2; +// } + private Entity spawnBossKing2() { GridPoint2 minPos = new GridPoint2(0, 0); GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); @@ -356,35 +457,33 @@ private Entity spawnBossKing2() { } /** - * Creates multiple projectiles that travel simultaneous. They all have same + * Creates multiple projectiles that travel simultaneous. They all have same * the starting point but different destinations. - * - * @param position The position of the Entity that's shooting the projectile. + * + * @param position The position of the Entity that's shooting the projectile. * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param space The space between the projectiles' destination. - * @param speed The speed of the projectiles. - * @param quantity The amount of projectiles to spawn. + * @param direction The direction the projectile should head towards. + * @param space The space between the projectiles' destination. + * @param speed The speed of the projectiles. + * @param quantity The amount of projectiles to spawn. */ - private void spawnMultiProjectile(Vector2 position, short targetLayer, int direction, int space, Vector2 speed, int quantity) { int half = quantity / 2; for (int i = 0; i < quantity; i++) { - spawnProjectile(position, targetLayer, space * half, direction, speed); - --half; + spawnProjectile(position, targetLayer, space * half, direction, speed); + --half; } } - /** * Returns projectile that can do an area of effect damage - * - * @param position The position of the Entity that's shooting the projectile. + * + * @param position The position of the Entity that's shooting the projectile. * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - * @param effect Type of effect. - * @param aoe Whether it is an aoe projectile. + * @param direction The direction the projectile should head towards. + * @param speed The speed of the projectiles. + * @param effect Type of effect. + * @param aoe Whether it is an aoe projectile. */ private void spawnEffectProjectile(Vector2 position, short targetLayer, int direction, Vector2 speed, ProjectileEffects effect, boolean aoe) { @@ -397,11 +496,11 @@ private void spawnEffectProjectile(Vector2 position, short targetLayer, int dire * Spawns a pierce fireball. * Pierce fireball can go through targetlayers without disappearing but damage * will still be applied. - * - * @param position The position of the Entity that's shooting the projectile. + * + * @param position The position of the Entity that's shooting the projectile. * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. + * @param direction The direction the projectile should head towards. + * @param speed The speed of the projectiles. */ private void spawnPierceFireBall(Vector2 position, short targetLayer, int direction, Vector2 speed) { Entity projectile = ProjectileFactory.createPierceFireBall(targetLayer, new Vector2(direction, position.y), speed); @@ -413,11 +512,11 @@ private void spawnPierceFireBall(Vector2 position, short targetLayer, int direct * Spawns a ricochet fireball * Ricochet fireballs bounce off targets with a specified maximum count of 3 * Possible extensions: Make the bounce count flexible with a param. - * - * @param position The position of the Entity that's shooting the projectile. + * + * @param position The position of the Entity that's shooting the projectile. * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. + * @param direction The direction the projectile should head towards. + * @param speed The speed of the projectiles. */ private void spawnRicochetFireball(Vector2 position, short targetLayer, int direction, Vector2 speed) { // Bounce count set to 0. @@ -429,12 +528,12 @@ private void spawnRicochetFireball(Vector2 position, short targetLayer, int dire /** * Spawns a split firework fireball. * Splits into mini projectiles that spreads out after collision. - * - * @param position The position of the Entity that's shooting the projectile. + * + * @param position The position of the Entity that's shooting the projectile. * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - * @param amount The amount of projectiles appearing after collision. + * @param direction The direction the projectile should head towards. + * @param speed The speed of the projectiles. + * @param amount The amount of projectiles appearing after collision. */ private void spawnSplitFireWorksFireBall(Vector2 position, short targetLayer, int direction, Vector2 speed, int amount) { Entity projectile = ProjectileFactory.createSplitFireWorksFireball(targetLayer, new Vector2(direction, position.y), speed, amount); @@ -469,18 +568,6 @@ private void spawnTNTTower() { } - private void spawnDroidTower() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); - - for (int i = 0; i < NUM_WEAPON_TOWERS; i++) { - GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity weaponTower = TowerFactory.createDroidTower(); - spawnEntityAt(weaponTower, randomPos, true, true); - } - - } - private void playMusic() { Music music = ServiceLocator.getResourceService().getAsset(backgroundMusic, Music.class); @@ -536,32 +623,59 @@ private void spawnScrap() { } } - private void spawnIncome() { GridPoint2 minPos = new GridPoint2(0, 0); GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 50; i++) { GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); Entity towerfactory = TowerFactory.createIncomeTower(); spawnEntityAt(towerfactory, randomPos, true, true); } } - /** - * Creates the scanners (one per lane) that detect absence of towers and presence of mobs, - * and trigger engineer spawning - */ - private void spawnGapScanners() { - for (int i = 0; i < terrain.getMapBounds(0).y; i++) { - Entity scanner = GapScannerFactory.createScanner(); - spawnEntityAt(scanner, new GridPoint2(0, i), true, true); + private void spawnEngineer() { + for (int i = 0; i < terrain.getMapBounds(0).x; i += 3) { + Entity engineer = EngineerFactory.createEngineer(); + spawnEntityAt(engineer, new GridPoint2(1, i), true, true); } + } + + /** + * Creates the scanners (one per lane) that detect absence of towers and presence of mobs, + * and trigger engineer spawning + */ +// private void spawnGapScanners() { +// for (int i = 0; i < terrain.getMapBounds(0).y; i++) { +// Entity scanner = GapScannerFactory.createScanner(); +// spawnEntityAt(scanner, new GridPoint2(0, i), true, true); +// } +// } // GridPoint2 minPos = new GridPoint2(0, 0); // GridPoint2 maxPos = new GridPoint2(5, terrain.getMapBounds(0).sub(2, 2).y); // GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); // // Entity engineer = EngineerFactory.createEngineer(); // spawnEntityAt(engineer, randomPos, true, true); - } +// } + + +// private void gameTrackerStart() { +// Entity endGameTracker = new Entity(); +// +// endGameTracker +// .addComponent(new CombatStatsComponent(2, 0)) +// .addComponent(new PlayerStatsDisplay()); +//// .getEvents().addListener("engineerKilled" , this::decrementCounter); +// endGameTracker.create(); +// } + +// private void decrementCounter() { +// this.endStateCounter -= 1; +// logger.info("Engineer killed"); +// if (endStateCounter <= 0) { +// // we've reached the end, game over +// this.dispose(); +// } +// } } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java b/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java index 30cf1b69d..80c93f434 100644 --- a/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java +++ b/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java @@ -137,5 +137,6 @@ public Weapon chooseWeapon(Fixture other) { } return weapon; } + } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java index 864f5a3f3..9f1159de4 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java @@ -152,8 +152,9 @@ public void updateTowerState() { } } case DIE -> { - if (owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) - owner.getEntity().setFlagForDelete(true); + if (owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { + owner.getEntity().setFlagForDelete(true); + } } } } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java index c70919487..e2f1327ef 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java @@ -3,12 +3,14 @@ import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.DefaultTask; import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.ProjectileEffects; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.ProjectileFactory; import com.csse3200.game.physics.PhysicsEngine; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.raycast.RaycastHit; +import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; @@ -21,9 +23,10 @@ public class FireTowerCombatTask extends DefaultTask implements PriorityTask { private static final int INTERVAL = 1; //time interval to scan for enemies in seconds private static final short TARGET = PhysicsLayer.NPC; //the type of targets this tower will detect //The constants are names of events that will be triggered in the state machine - private static final String IDLE = "startIdle"; - private static final String PREP_ATTACK = "startAttackPrep"; - private static final String ATTACK = "startAttack"; + public static final String IDLE = "startIdle"; + public static final String PREP_ATTACK = "startAttackPrep"; + public static final String ATTACK = "startAttack"; + public static final String DEATH = "startDeath"; //Class attributes private final int priority; @@ -36,10 +39,10 @@ public class FireTowerCombatTask extends DefaultTask implements PriorityTask { private long endTime; private final RaycastHit hit = new RaycastHit(); - private enum STATE { - IDLE, PREP_ATTACK, ATTACK + public enum STATE { + IDLE, PREP_ATTACK, ATTACK, DEATH } - private STATE towerState = STATE.IDLE; + public STATE towerState = STATE.IDLE; /** * Starts the task running, triggers the initial 'IDLE' event @@ -81,6 +84,11 @@ public void update() { * finite state machine for the FireTower. Detects mobs in a straight line and changes the state of the tower. */ public void updateTowerState() { + if (owner.getEntity().getComponent(CombatStatsComponent.class).getHealth() <= 0 && towerState != STATE.DEATH) { + owner.getEntity().getEvents().trigger(DEATH); + towerState = STATE.DEATH; + return; + } switch (towerState) { case IDLE -> { if (isTargetVisible()) { @@ -110,6 +118,11 @@ public void updateTowerState() { ServiceLocator.getEntityService().register(newProjectile); } } + case DEATH -> { + if (owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { + owner.getEntity().setFlagForDelete(true); + } + } } } @@ -121,6 +134,13 @@ public void stop() { owner.getEntity().getEvents().trigger(IDLE); } + /** + * @return returns the current state of the tower + */ + public STATE getState() { + return this.towerState; + } + /** * gets the priority for the current task. * @return (int) active priority if target is visible and inactive priority otherwise diff --git a/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java index 2340ac3e6..be56625f7 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java @@ -3,12 +3,14 @@ import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.DefaultTask; import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.ProjectileEffects; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.ProjectileFactory; import com.csse3200.game.physics.PhysicsEngine; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.raycast.RaycastHit; +import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; @@ -24,6 +26,7 @@ public class StunTowerCombatTask extends DefaultTask implements PriorityTask { //Following constants are names of events that will be triggered in the state machine public static final String IDLE = "startIdle"; public static final String ATTACK = "startAttack"; + public static final String DEATH = "startDeath"; //Following are the class constants private final int priority; @@ -36,10 +39,10 @@ public class StunTowerCombatTask extends DefaultTask implements PriorityTask { private final RaycastHit hit = new RaycastHit(); //enums for the state triggers - private enum STATE { - IDLE, ATTACK + public enum STATE { + IDLE, ATTACK, DIE } - private STATE towerState = STATE.IDLE; + public STATE towerState = STATE.IDLE; /** * @param priority Task priority when targets are detected (0 when nothing is present) @@ -83,6 +86,14 @@ public void update() { * of the game. If enemies are detected, state of the tower is changed to attack state. */ public void updateTowerState() { + + if (owner.getEntity().getComponent(CombatStatsComponent.class).getHealth() <= 0 && + towerState != STATE.DIE) { + owner.getEntity().getEvents().trigger(DEATH); + towerState = STATE.DIE; + return; + } + switch (towerState) { case IDLE -> { if(isTargetVisible()) { @@ -96,17 +107,28 @@ public void updateTowerState() { towerState = STATE.IDLE; } else { owner.getEntity().getEvents().trigger(ATTACK); - Entity newProjectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100, - owner.getEntity().getPosition().y), new Vector2(2,2), ProjectileEffects.STUN, false); - newProjectile.setScale(-0.4f, -0.4f); +// Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, +// new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f, 2f)); + Entity newProjectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, + new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f, 2f), + ProjectileEffects.STUN, false); newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.25), - (float) (owner.getEntity().getPosition().y + 0.85)); + (float) (owner.getEntity().getPosition().y + 0.25)); ServiceLocator.getEntityService().register(newProjectile); } } + case DIE -> { + if (owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { + owner.getEntity().setFlagForDelete(true); + } + } } } + public STATE getState() { + return this.towerState; + } + /** * stops the current animation and switches back the state of the tower to IDLE. */ diff --git a/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java index 358d7a3a6..6753b6ba1 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java @@ -14,11 +14,13 @@ public class FireTowerAnimationController extends Component{ private static final String IDLE = "startIdle"; private static final String PREP_ATTACK = "startAttackPrep"; private static final String ATTACK = "startAttack"; + private static final String DEATH = "startDeath"; //animation name constants private static final String IDLE_ANIM = "idle"; private static final String PREP_ATTACK_ANIM = "prepAttack"; private static final String ATTACK_ANIM = "attack"; + private static final String DEATH_ANIM = "death"; //here we can add the sounds for the implemented animations AnimationRenderComponent animator; @@ -33,6 +35,7 @@ public void create() { entity.getEvents().addListener(IDLE, this::animateIdle); entity.getEvents().addListener(PREP_ATTACK, this::animatePrepAttack); entity.getEvents().addListener(ATTACK, this::animateAttack); + entity.getEvents().addListener(DEATH, this::animateDeath); } /** @@ -55,4 +58,8 @@ void animatePrepAttack() { void animateAttack() { animator.startAnimation(ATTACK_ANIM); } + + void animateDeath() { + animator.startAnimation(DEATH_ANIM); + } } diff --git a/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java index fa4868c4c..846d344dd 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java @@ -12,9 +12,11 @@ public class StunTowerAnimationController extends Component { //Event name constants private static final String IDLE = "startIdle"; private static final String ATTACK = "startAttack"; + private static final String DEATH = "startDeath"; //animation name constants private static final String IDLE_ANIM = "idle"; private static final String ATTACK_ANIM = "attack"; + private static final String DEATH_ANIM = "death"; //further sounds can be added for the tower attacks/movement @@ -30,6 +32,7 @@ public void create() { animator = this.entity.getComponent(AnimationRenderComponent.class); entity.getEvents().addListener(IDLE, this::animateIdle); entity.getEvents().addListener(ATTACK, this::animateAttack); + entity.getEvents().addListener(DEATH, this::animateDeath); } /** @@ -45,4 +48,11 @@ void animateIdle() { void animateAttack() { animator.startAnimation(ATTACK_ANIM); } + + /** + * starts the death animation + */ + void animateDeath() { + animator.startAnimation(DEATH_ANIM); + } } diff --git a/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java index 1b3eab1ee..968f4ec93 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java @@ -7,9 +7,8 @@ public class baseTowerConfigs { public WeaponTowerConfig weapon = new WeaponTowerConfig(); public WallTowerConfig wall = new WallTowerConfig(); public IncomeTowerConfig income = new IncomeTowerConfig(); - public TNTTowerConfigs TNTTower = new TNTTowerConfigs(); - public DroidTowerConfig DroidTower = new DroidTowerConfig(); public FireTowerConfig fireTower = new FireTowerConfig(); public StunTowerConfig stunTower = new StunTowerConfig(); - + public TNTTowerConfigs TNTTower = new TNTTowerConfigs(); + public DroidTowerConfig DroidTower = new DroidTowerConfig(); } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index 9fe31184b..1341746fd 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -1,19 +1,11 @@ package com.csse3200.game.entities.factories; - - import com.csse3200.game.components.tasks.DroidCombatTask; import com.csse3200.game.components.tasks.TNTTowerCombatTask; -import com.csse3200.game.components.tower.DroidAnimationController; -import com.csse3200.game.components.tower.TNTAnimationController; -import com.csse3200.game.components.tower.TNTDamageComponent; +import com.csse3200.game.components.tower.*; import com.csse3200.game.entities.configs.*; import com.csse3200.game.components.tasks.FireTowerCombatTask; import com.csse3200.game.components.tasks.StunTowerCombatTask; -import com.csse3200.game.components.tower.FireTowerAnimationController; -import com.csse3200.game.components.tower.StunTowerAnimationController; -import com.csse3200.game.components.tower.TowerUpgraderComponent; - import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; @@ -21,8 +13,6 @@ import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.CostComponent; import com.csse3200.game.components.tasks.TowerCombatTask; -import com.csse3200.game.components.tower.EconTowerAnimationController; -import com.csse3200.game.components.tower.TowerAnimationController; import com.csse3200.game.components.tasks.CurrencyTask; import com.csse3200.game.entities.Entity; import com.csse3200.game.physics.PhysicsLayer; @@ -35,8 +25,6 @@ import com.csse3200.game.rendering.TextureRenderComponent; import com.csse3200.game.services.ServiceLocator; -import java.util.ServiceConfigurationError; - /** * Factory to create a tower entity. * @@ -53,6 +41,8 @@ public class TowerFactory { private static final String WALL_IMAGE = "images/towers/wallTower.png"; private static final String RESOURCE_TOWER = "images/towers/mine_tower.png"; private static final String TURRET_ATLAS = "images/towers/turret01.atlas"; + private static final String FIRE_TOWER_ATLAS = "images/towers/fire_tower_atlas.atlas"; + private static final String STUN_TOWER_ATLAS = "images/towers/stun_tower.atlas"; private static final String TNT_ATLAS = "images/towers/TNTTower.atlas"; private static final String DROID_ATLAS = "images/towers/DroidTower.atlas"; private static final float DROID_SPEED = 0.25f; @@ -68,9 +58,6 @@ public class TowerFactory { private static final float DIG_SPEED = 0.2f; private static final String EXPLODE_ANIM = "explode"; private static final float EXPLODE_SPEED = 0.2f; - private static final String FIRE_TOWER_ATLAS = "images/towers/fire_tower_atlas.atlas"; - private static final String STUN_TOWER_ATLAS = "images/towers/stun_tower.atlas"; - private static final String IDLE_ANIM = "idle"; private static final float IDLE_SPEED = 0.3f; private static final String DEPLOY_ANIM = "deploy"; @@ -79,22 +66,22 @@ public class TowerFactory { private static final float STOW_SPEED = 0.2f; private static final String FIRE_ANIM = "firing"; private static final float FIRE_SPEED = 0.25f; - private static final String FIRE_TOWER_IDLE_ANIM = "idle"; private static final float FIRE_TOWER_IDLE_SPEED = 0.3f; private static final String FIRE_TOWER_PREP_ATTACK_ANIM = "prepAttack"; private static final float FIRE_TOWER_PREP_ATTACK_SPEED = 0.2f; private static final String FIRE_TOWER_ATTACK_ANIM = "attack"; private static final float FIRE_TOWER_ATTACK_SPEED = 0.25f; + private static final String FIRE_TOWER_DEATH_ANIM = "death"; + private static final float FIRE_TOWER_DEATH_SPEED = 0.12f; private static final String STUN_TOWER_IDLE_ANIM = "idle"; private static final float STUN_TOWER_IDLE_SPEED = 0.33f; private static final String STUN_TOWER_ATTACK_ANIM = "attack"; private static final float STUN_TOWER_ATTACK_SPEED = 0.12f; + private static final String STUN_TOWER_DEATH_ANIM = "death"; + private static final float STUN_TOWER_DEATH_SPEED = 0.12f; private static final int INCOME_INTERVAL = 300; - private static final int INCOME_TASK_PRIORITY = 1; - - private static final String ECO_ATLAS = "images/economy/econ-tower.atlas"; private static final String ECO_MOVE = "move1"; private static final String ECO_IDLE = "idle"; @@ -274,6 +261,7 @@ public static Entity createFireTower() { animator.addAnimation(FIRE_TOWER_IDLE_ANIM, FIRE_TOWER_IDLE_SPEED, Animation.PlayMode.LOOP); animator.addAnimation(FIRE_TOWER_PREP_ATTACK_ANIM, FIRE_TOWER_PREP_ATTACK_SPEED, Animation.PlayMode.NORMAL); animator.addAnimation(FIRE_TOWER_ATTACK_ANIM, FIRE_TOWER_ATTACK_SPEED, Animation.PlayMode.LOOP); + animator.addAnimation(FIRE_TOWER_DEATH_ANIM, FIRE_TOWER_DEATH_SPEED, Animation.PlayMode.NORMAL); fireTower .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) @@ -302,6 +290,7 @@ public static Entity createStunTower() { .getAsset(STUN_TOWER_ATLAS, TextureAtlas.class)); animator.addAnimation(STUN_TOWER_IDLE_ANIM, STUN_TOWER_IDLE_SPEED, Animation.PlayMode.LOOP); animator.addAnimation(STUN_TOWER_ATTACK_ANIM, STUN_TOWER_ATTACK_SPEED, Animation.PlayMode.LOOP); + animator.addAnimation(STUN_TOWER_DEATH_ANIM, STUN_TOWER_DEATH_SPEED, Animation.PlayMode.NORMAL); stunTower .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) @@ -323,11 +312,9 @@ public static Entity createBaseTower() { // we're going to add more components later on Entity tower = new Entity() .addComponent(new ColliderComponent()) + .addComponent(new HitboxComponent().setLayer(PhysicsLayer.TOWER)) // TODO: we might have to change the names of the layers .addComponent(new PhysicsComponent().setBodyType(BodyType.StaticBody)) - .addComponent(new TowerUpgraderComponent()) - .addComponent(new HitboxComponent().setLayer(PhysicsLayer.TOWER)); // TODO: we might have to change the names of the layers - - + .addComponent(new TowerUpgraderComponent()); return tower; } diff --git a/source/core/src/test/com/csse3200/game/components/tasks/FireTowerCombatTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/FireTowerCombatTaskTest.java new file mode 100644 index 000000000..dc7394399 --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/FireTowerCombatTaskTest.java @@ -0,0 +1,139 @@ +package com.csse3200.game.components.tasks; + +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.EntityService; +import com.csse3200.game.events.listeners.EventListener0; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.physics.components.ColliderComponent; +import com.csse3200.game.physics.components.HitboxComponent; +import com.csse3200.game.physics.components.PhysicsComponent; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.entities.factories.ProjectileFactory; + +import jdk.jfr.Event; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class FireTowerCombatTaskTest { + FireTowerCombatTask fireTowerCombatTask; + + @BeforeEach + void setUp() { + GameTime gameTime = mock(GameTime.class); + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + ServiceLocator.registerEntityService(new EntityService()); + fireTowerCombatTask = new FireTowerCombatTask(1, 4); + } + + /** + * this test has been implement using the same logic as the tests implemented + * in DroidCombatTaskTest by Mohamad Dabboussi + */ + @Test + public void testStartTriggersIdleEvent() { + Entity entity = createFireTower(); + EventListener0 idleListener = mock(EventListener0.class); + // Deploy Droid in the walking state + entity.getEvents().addListener(FireTowerCombatTask.IDLE, idleListener); + fireTowerCombatTask.start(); + verify(idleListener).handle(); + } + + /** + * this test has been implement using the same logic as the tests implemented + * in DroidCombatTaskTest by Mohamad Dabboussi + */ + @Test + public void testUpdateTowerStateWithTargetInRange() { + Entity entity = createFireTower(); + entity.setPosition(10, 10); + + Entity target = createNPC(); + target.setPosition(12, 10); + + EventListener0 prepAttack = mock(EventListener0.class); + EventListener0 attack = mock(EventListener0.class); + entity.getEvents().addListener(FireTowerCombatTask.PREP_ATTACK, prepAttack); + entity.getEvents().addListener(FireTowerCombatTask.ATTACK, attack); + //Jump to IDLE state + fireTowerCombatTask.start(); + fireTowerCombatTask.towerState = FireTowerCombatTask.STATE.IDLE; + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + + assertTrue(fireTowerCombatTask.isTargetVisible()); + + fireTowerCombatTask.updateTowerState(); + verify(prepAttack).handle(); + assertEquals(FireTowerCombatTask.STATE.PREP_ATTACK, fireTowerCombatTask.getState()); + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + assertTrue(fireTowerCombatTask.isTargetVisible()); + + fireTowerCombatTask.updateTowerState(); + verify(attack).handle(); + assertEquals(FireTowerCombatTask.STATE.ATTACK, fireTowerCombatTask.getState()); + } + + /** + * this test has been implement using the same logic as the tests implemented + * in DroidCombatTaskTest by Mohamad Dabboussi + */ + @Test + public void testUpdateTowerStateWithTargetNotInRange() { + Entity entity = createFireTower(); + entity.setPosition(10, 10); + + Entity target = createNPC(); + target.setPosition(15, 10); + + EventListener0 idle = mock(EventListener0.class); + EventListener0 prepAttack = mock(EventListener0.class); + entity.getEvents().addListener(FireTowerCombatTask.IDLE, idle); + entity.getEvents().addListener(FireTowerCombatTask.PREP_ATTACK, prepAttack); + + fireTowerCombatTask.towerState = FireTowerCombatTask.STATE.IDLE; + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + assertFalse(fireTowerCombatTask.isTargetVisible()); + + fireTowerCombatTask.updateTowerState(); + + verify(idle).handle(); + verifyNoInteractions(prepAttack); + assertEquals(FireTowerCombatTask.STATE.IDLE, fireTowerCombatTask.getState()); + } + + Entity createFireTower() { + AITaskComponent aiTaskComponent = new AITaskComponent().addTask(fireTowerCombatTask); + Entity entity = new Entity().addComponent(aiTaskComponent) + .addComponent(new PhysicsComponent()) + .addComponent(new HitboxComponent()) + .addComponent(new ColliderComponent()) + .addComponent(new CombatStatsComponent(100,10)); + entity.create(); + return entity; + } + + Entity createNPC() { + Entity Target = new Entity().addComponent(new HitboxComponent().setLayer(PhysicsLayer.NPC)) + .addComponent(new ColliderComponent()) + .addComponent(new PhysicsComponent()); + Target.create(); + return Target; + } +} diff --git a/source/core/src/test/com/csse3200/game/components/tasks/StunTowerCombatTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/StunTowerCombatTaskTest.java new file mode 100644 index 000000000..37c87158c --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/StunTowerCombatTaskTest.java @@ -0,0 +1,129 @@ +package com.csse3200.game.components.tasks; + +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; + +import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.EntityService; +import com.csse3200.game.events.listeners.EventListener0; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.physics.components.ColliderComponent; +import com.csse3200.game.physics.components.HitboxComponent; +import com.csse3200.game.physics.components.PhysicsComponent; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.entities.factories.ProjectileFactory; + +import jdk.jfr.Event; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class StunTowerCombatTaskTest { + StunTowerCombatTask stunTowerCombatTask; + + @BeforeEach + void setUp() { + GameTime gameTime = mock(GameTime.class); + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + ServiceLocator.registerEntityService(new EntityService()); + stunTowerCombatTask = new StunTowerCombatTask(1, 4); + } + + /** + * this test has been implement using the same logic as the tests implemented + * in DroidCombatTaskTest by Mohamad Dabboussi + */ + @Test + public void testStartTriggersIdleEvent() { + Entity entity = createStunTower(); + EventListener0 idleListener = mock(EventListener0.class); + // Deploy Droid in the walking state + entity.getEvents().addListener(StunTowerCombatTask.IDLE, idleListener); + stunTowerCombatTask.start(); + verify(idleListener).handle(); + } + + /** + * this test has been implement using the same logic as the tests implemented + * in DroidCombatTaskTest by Mohamad Dabboussi + */ + @Test + public void testUpdateTowerStateWithTargetInRange() { + Entity entity = createStunTower(); + entity.setPosition(10, 10); + + Entity target = createNPC(); + target.setPosition(12, 10); + + EventListener0 attack = mock(EventListener0.class); + entity.getEvents().addListener(StunTowerCombatTask.ATTACK, attack); + //Jump to IDLE state + stunTowerCombatTask.start(); + stunTowerCombatTask.towerState = StunTowerCombatTask.STATE.IDLE; + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + + assertTrue(stunTowerCombatTask.isTargetVisible()); + + stunTowerCombatTask.updateTowerState(); + verify(attack).handle(); + assertEquals(StunTowerCombatTask.STATE.ATTACK, stunTowerCombatTask.getState()); + } + + /** + * this test has been implement using the same logic as the tests implemented + * in DroidCombatTaskTest by Mohamad Dabboussi + */ + @Test + public void testUpdateTowerStateWithTargetNotInRange() { + Entity entity = createStunTower(); + entity.setPosition(10, 10); + + Entity target = createNPC(); + target.setPosition(15, 10); + + EventListener0 idle = mock(EventListener0.class); + EventListener0 attack = mock(EventListener0.class); + entity.getEvents().addListener(StunTowerCombatTask.IDLE, idle); + entity.getEvents().addListener(StunTowerCombatTask.ATTACK, attack); + + stunTowerCombatTask.towerState = StunTowerCombatTask.STATE.IDLE; + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + assertFalse(stunTowerCombatTask.isTargetVisible()); + + stunTowerCombatTask.updateTowerState(); + + verify(idle).handle(); + verifyNoInteractions(attack); + assertEquals(StunTowerCombatTask.STATE.IDLE, stunTowerCombatTask.getState()); + } + + Entity createStunTower() { + AITaskComponent aiTaskComponent = new AITaskComponent().addTask(stunTowerCombatTask); + Entity entity = new Entity().addComponent(aiTaskComponent) + .addComponent(new PhysicsComponent()) + .addComponent(new HitboxComponent()) + .addComponent(new ColliderComponent()) + .addComponent(new CombatStatsComponent(100,10)); + entity.create(); + return entity; + } + + Entity createNPC() { + Entity Target = new Entity().addComponent(new HitboxComponent().setLayer(PhysicsLayer.NPC)) + .addComponent(new ColliderComponent()) + .addComponent(new PhysicsComponent()); + Target.create(); + return Target; + } +} diff --git a/source/core/src/test/com/csse3200/game/components/tower/FireTowerAnimationControllerTest.java b/source/core/src/test/com/csse3200/game/components/tower/FireTowerAnimationControllerTest.java new file mode 100644 index 000000000..6f62eb3be --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tower/FireTowerAnimationControllerTest.java @@ -0,0 +1,66 @@ +package com.csse3200.game.components.tower; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.TowerFactory; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.rendering.DebugRenderer; +import com.csse3200.game.rendering.RenderService; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(GameExtension.class) +public class FireTowerAnimationControllerTest { + + private Entity mockEntity; + private final String[] texture = {"images/towers/fire_tower_atlas.png"}; + private final String[] atlas = {"images/towers/fire_tower_atlas.atlas"}; + + @BeforeEach + public void setUp() { + ServiceLocator.registerPhysicsService(new PhysicsService()); + RenderService render = new RenderService(); + render.setDebug(mock(DebugRenderer.class)); + ServiceLocator.registerRenderService(render); + ResourceService resourceService = new ResourceService(); + ServiceLocator.registerResourceService(resourceService); + resourceService.loadTextures(texture); + resourceService.loadTextureAtlases(atlas); + resourceService.loadAll(); + + mockEntity = TowerFactory.createFireTower(); // Replace with actual Droid Tower creation logic + mockEntity.create(); + } + + @Test + public void testAnimateWalk() { + mockEntity.getEvents().trigger("startIdle"); + assertEquals("idle", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateDefault() { + mockEntity.getEvents().trigger("startAttackPrep"); + assertEquals("prepAttack", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateGoUp() { + mockEntity.getEvents().trigger("startAttack"); + assertEquals("attack", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateDeath() { + mockEntity.getEvents().trigger("startDeath"); + assertEquals("death", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } +} diff --git a/source/core/src/test/com/csse3200/game/components/tower/StunTowerAnimationControllerTest.java b/source/core/src/test/com/csse3200/game/components/tower/StunTowerAnimationControllerTest.java new file mode 100644 index 000000000..dbb7df351 --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tower/StunTowerAnimationControllerTest.java @@ -0,0 +1,60 @@ +package com.csse3200.game.components.tower; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.TowerFactory; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.rendering.DebugRenderer; +import com.csse3200.game.rendering.RenderService; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(GameExtension.class) +public class StunTowerAnimationControllerTest { + + private Entity mockEntity; + private final String[] texture = {"images/towers/stun_tower.png"}; + private final String[] atlas = {"images/towers/stun_tower.atlas"}; + + @BeforeEach + public void setUp() { + ServiceLocator.registerPhysicsService(new PhysicsService()); + RenderService render = new RenderService(); + render.setDebug(mock(DebugRenderer.class)); + ServiceLocator.registerRenderService(render); + ResourceService resourceService = new ResourceService(); + ServiceLocator.registerResourceService(resourceService); + resourceService.loadTextures(texture); + resourceService.loadTextureAtlases(atlas); + resourceService.loadAll(); + + mockEntity = TowerFactory.createStunTower(); // Replace with actual Droid Tower creation logic + mockEntity.create(); + } + + @Test + public void testAnimateWalk() { + mockEntity.getEvents().trigger("startIdle"); + assertEquals("idle", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateDefault() { + mockEntity.getEvents().trigger("startAttack"); + assertEquals("attack", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateGoUp() { + mockEntity.getEvents().trigger("startDeath"); + assertEquals("death", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } +} diff --git a/source/core/src/test/com/csse3200/game/entities/factories/TowerFactoryTest.java b/source/core/src/test/com/csse3200/game/entities/factories/TowerFactoryTest.java index 0924d2667..1ab5d51cc 100644 --- a/source/core/src/test/com/csse3200/game/entities/factories/TowerFactoryTest.java +++ b/source/core/src/test/com/csse3200/game/entities/factories/TowerFactoryTest.java @@ -39,12 +39,26 @@ public class TowerFactoryTest { private Entity baseTower; private Entity weaponTower; private Entity wallTower; + private Entity stunTower; + private Entity fireTower; + private Entity tntTower; + private Entity droidTower; private String[] texture = { "images/towers/turret_deployed.png", "images/towers/turret01.png", - "images/towers/wallTower.png" + "images/towers/wallTower.png", + "images/towers/fire_tower_atlas.png", + "images/towers/stun_tower.png", + "images/towers/DroidTower.png", + "images/towers/TNTTower.png" + }; + private String[] atlas = { + "images/towers/turret01.atlas", + "images/towers/stun_tower.atlas", + "images/towers/fire_tower_atlas.atlas", + "images/towers/DroidTower.atlas", + "images/towers/TNTTower.atlas" }; - private String[] atlas = {"images/towers/turret01.atlas"}; private static final String[] sounds = { "sounds/towers/gun_shot_trimmed.mp3", "sounds/towers/deploy.mp3", @@ -71,6 +85,10 @@ public void setUp() { baseTower = TowerFactory.createBaseTower(); weaponTower = TowerFactory.createWeaponTower(); wallTower = TowerFactory.createWallTower(); + fireTower = TowerFactory.createFireTower(); + stunTower = TowerFactory.createFireTower(); + tntTower = TowerFactory.createTNTTower(); + droidTower = TowerFactory.createDroidTower(); } @Test @@ -79,6 +97,10 @@ public void testCreateBaseTowerNotNull() { assertNotNull(baseTower, "Base tower should not be null"); assertNotNull(weaponTower, "Weaponry tower should not be null"); assertNotNull(wallTower, "Wall tower should not be null"); + assertNotNull(stunTower, "Stun tower must not be null"); + assertNotNull(fireTower, "Fire tower must not be null"); + assertNotNull(tntTower, "TNT tower must not be null"); + assertNotNull(droidTower, "Droid tower must not be null"); } @Test @@ -89,6 +111,14 @@ public void testCreateBaseTowerHasColliderComponent() { "Weaponry tower should have ColliderComponent"); assertNotNull(wallTower.getComponent(ColliderComponent.class), "Wall tower should have ColliderComponent"); + assertNotNull(stunTower.getComponent(ColliderComponent.class), + "Stun Tower should have ColliderComponent"); + assertNotNull(fireTower.getComponent(ColliderComponent.class), + "Fire tower should have ColliderComponent"); + assertNotNull(tntTower.getComponent(ColliderComponent.class), + "TNT tower should have ColliderComponent"); + assertNotNull(droidTower.getComponent(ColliderComponent.class), + "Droid tower should have ColliderComponent"); } @Test @@ -99,6 +129,14 @@ public void testCreateBaseTowerHasHitboxComponent() { "Weaponry tower should have HitboxComponent"); assertNotNull(wallTower.getComponent(HitboxComponent.class), "Wall tower should have HitboxComponent"); + assertNotNull(stunTower.getComponent(HitboxComponent.class), + "Stun tower should have HitboxComponent"); + assertNotNull(fireTower.getComponent(HitboxComponent.class), + "Fire tower should have HitboxComponent"); + assertNotNull(tntTower.getComponent(HitboxComponent.class), + "TNT tower should have HitboxComponent"); + assertNotNull(droidTower.getComponent(HitboxComponent.class), + "Droid tower should have HitboxComponent"); } @Test @@ -109,6 +147,14 @@ public void testCreateBaseTowerHasPhysicsComponent() { "Weaponry tower should have PhysicsComponent"); assertNotNull(wallTower.getComponent(PhysicsComponent.class), "Wall tower should have PhysicsComponent"); + assertNotNull(stunTower.getComponent(PhysicsComponent.class), + "Stun tower should have PhysicsComponent"); + assertNotNull(fireTower.getComponent(PhysicsComponent.class), + "Fire tower should have PhysicsComponent"); + assertNotNull(tntTower.getComponent(PhysicsComponent.class), + "TNT tower should have PhysicsComponent"); + assertNotNull(droidTower.getComponent(PhysicsComponent.class), + "Droid tower should have PhysicsComponent"); } @Test @@ -116,6 +162,10 @@ public void testCreateBaseTowerPhysicsComponentStaticBody() { PhysicsComponent physicsComponent = baseTower.getComponent(PhysicsComponent.class); PhysicsComponent physicsComponent1 = weaponTower.getComponent(PhysicsComponent.class); PhysicsComponent physicsComponent2 = wallTower.getComponent(PhysicsComponent.class); + PhysicsComponent physicsComponent3 = stunTower.getComponent(PhysicsComponent.class); + PhysicsComponent physicsComponent4 = fireTower.getComponent(PhysicsComponent.class); + PhysicsComponent physicsComponent5 = tntTower.getComponent(PhysicsComponent.class); + PhysicsComponent physicsComponent6 = droidTower.getComponent(PhysicsComponent.class); assertTrue(physicsComponent.getBody().getType() == BodyType.StaticBody, "PhysicsComponent should be of type StaticBody"); @@ -123,18 +173,49 @@ public void testCreateBaseTowerPhysicsComponentStaticBody() { "PhysicsComponent1 should be of type StaticBody"); assertTrue(physicsComponent2.getBody().getType() == BodyType.StaticBody, "PhysicsComponent2 should be of type StaticBody"); + assertTrue(physicsComponent3.getBody().getType() == BodyType.StaticBody, + "StunTower's PhysicsComponent should be of type StaticBody"); + assertTrue(physicsComponent4.getBody().getType() == BodyType.StaticBody, + "FireTower's PhysicsComponent should be of type StaticBody"); + assertTrue(physicsComponent5.getBody().getType() == BodyType.StaticBody, + "TNT tower's PhysicsComponent should be of type StaticBody"); + assertTrue(physicsComponent6.getBody().getType() == BodyType.StaticBody, + "Droid Tower's PhysicsComponent should be of type StaticBody"); } @Test public void testWeaponTowerCombatStatsComponentAndCostComponent() { - assertTrue(weaponTower.getComponent(CombatStatsComponent.class).getHealth() == 10, + assertEquals(10, weaponTower.getComponent(CombatStatsComponent.class).getHealth(), "Health should be 10"); - assertTrue(weaponTower.getComponent(CombatStatsComponent.class).getBaseAttack() == 10, + assertEquals(10, weaponTower.getComponent(CombatStatsComponent.class).getBaseAttack(), "BaseAttack should be 10"); - assertTrue(weaponTower.getComponent(CostComponent.class).getCost() == 10, + assertEquals(10, weaponTower.getComponent(CostComponent.class).getCost(), "Cost should be 10"); - + assertEquals(10, fireTower.getComponent(CombatStatsComponent.class).getHealth(), + "Fire Tower health must be 10"); + assertEquals(10, fireTower.getComponent(CombatStatsComponent.class).getBaseAttack(), + "Fire Tower base attack must be 10"); + assertEquals(10, fireTower.getComponent(CostComponent.class).getCost(), + "Fire Tower cost must 10"); + assertEquals(10, stunTower.getComponent(CombatStatsComponent.class).getHealth(), + "Stun Tower health must be 10"); + assertEquals(10, stunTower.getComponent(CombatStatsComponent.class).getBaseAttack(), + "Stun Tower base attack must be 10"); + assertEquals(10, stunTower.getComponent(CostComponent.class).getCost(), + "Stun Tower cost must 10"); + assertEquals(10, tntTower.getComponent(CombatStatsComponent.class).getHealth(), + "TNT Tower health must be 10"); + assertEquals(5, tntTower.getComponent(CombatStatsComponent.class).getBaseAttack(), + "TNT Tower base attack must be 5"); + assertEquals(1, tntTower.getComponent(CostComponent.class).getCost(), + "TNT Tower cost must 1"); + assertEquals(50, droidTower.getComponent(CombatStatsComponent.class).getHealth(), + "TNT Tower health must be 50"); + assertEquals(5, droidTower.getComponent(CombatStatsComponent.class).getBaseAttack(), + "Droid Tower base attack must be 5"); + assertEquals(1, droidTower.getComponent(CostComponent.class).getCost(), + "Droid Tower cost must 1"); } @Test @@ -152,6 +233,10 @@ public void testWallTowerCombatStatsComponentAndCostComponent() { @Test public void weaponTowerHasAnimationComponent() { assertNotNull(weaponTower.getComponent(AnimationRenderComponent.class)); + assertNotNull(stunTower.getComponent(AnimationRenderComponent.class)); + assertNotNull(fireTower.getComponent(AnimationRenderComponent.class)); + assertNotNull(tntTower.getComponent(AnimationRenderComponent.class)); + assertNotNull(droidTower.getComponent(AnimationRenderComponent.class)); } @Test