From 1b26c612f5d40bfc92f893965e6d9c841c69830c Mon Sep 17 00:00:00 2001 From: Mohamad Date: Sun, 10 Sep 2023 18:51:28 +1000 Subject: [PATCH 1/3] Created an event for shooting projectiles in DroidAnimationController rather than calling it manually in the combat task --- .../components/tasks/TNTTowerCombatTask.java | 23 ++-- .../tower/DroidAnimationController.java | 36 ++++++ .../tasks/TNTTowerCombatTaskTest.java | 118 ++++++++++++++++++ 3 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 source/core/src/test/com/csse3200/game/components/tasks/TNTTowerCombatTaskTest.java diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java index c55a92450..a59361481 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java @@ -22,10 +22,10 @@ public class TNTTowerCombatTask 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 that the tower will detect // the following four constants are the event names that will be triggered in the state machine - private static final String DIG = "digStart"; - private static final String EXPLOSION = "explodeStart"; - private static final String DEFAULT = "defaultStart"; - private static final String DAMAGE = "TNTDamageStart"; + public static final String DIG = "digStart"; + public static final String EXPLOSION = "explodeStart"; + public static final String DEFAULT = "defaultStart"; + public static final String DAMAGE = "TNTDamageStart"; // class attributes @@ -37,9 +37,9 @@ public class TNTTowerCombatTask extends DefaultTask implements PriorityTask { private final GameTime timeSource; private long endTime; private final RaycastHit hit = new RaycastHit(); - private boolean readToDelete = false; + public boolean readToDelete = false; - private enum STATE { + public enum STATE { IDLE, EXPLODE, REMOVE } private STATE towerState = STATE.IDLE; @@ -135,6 +135,15 @@ public int getPriority() { } } + /** + * Returns the current state of the tower. + * + * @return the current state of the tower. + */ + public STATE getState() { + return this.towerState; + } + /** * Fetches the active priority of the Task if a target is visible. * @return (int) active priority if a target is visible, -1 otherwise @@ -157,7 +166,7 @@ private int getInactivePriority() { * Uses a raycast to determine whether there are any targets in detection range * @return true if a target is visible, false otherwise */ - private boolean isTargetVisible() { + public boolean isTargetVisible() { // If there is an obstacle in the path to the max range point, mobs visible. return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); } diff --git a/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java index be123d483..d8307e0e4 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java @@ -1,7 +1,13 @@ package com.csse3200.game.components.tower; +import com.badlogic.gdx.math.Vector2; import com.csse3200.game.components.Component; +import com.csse3200.game.components.ProjectileEffects; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.ProjectileFactory; +import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.services.ServiceLocator; /** * This class listens to events relevant to DroidTower entity's state and plays the animation when one @@ -25,6 +31,8 @@ public void create() { entity.getEvents().addListener("attackUpStart",this::animateAttackUp); entity.getEvents().addListener("attackDownStart",this::animateAttackDown); entity.getEvents().addListener("deathStart",this::animateDeath); + entity.getEvents().addListener("ShootUp",this::shootUp); + entity.getEvents().addListener("ShootDown",this::shootDown); } @@ -83,4 +91,32 @@ void animateDeath() { */ void animateDefault() { animator.startAnimation("idle");} + + //TODO: For the time being, these items will be positioned here. Next, we should create a component that enables an entity to fire projectiles. + + /** + * Fires a projectile upwards from the entity's current position. + */ + void shootUp() { + Entity Projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100, + entity.getPosition().y), new Vector2(2,2), ProjectileEffects.SLOW, false); + Projectile.setScale(new Vector2(0.5f,0.5f)); + Projectile.setPosition((float) (entity.getPosition().x + 0.2), + (float) (entity.getPosition().y + 0.5)); + ServiceLocator.getEntityService().register(Projectile); + } + + /** + * Fires a projectile downwards from the entity's current position. + */ + void shootDown() { + Entity Projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100, + entity.getPosition().y), new Vector2(2,2), ProjectileEffects.SLOW, false); + Projectile.setScale(new Vector2(0.5f,0.5f)); + Projectile.setPosition((float) (entity.getPosition().x + 0.2), + (float) (entity.getPosition().y - 0.2)); + ServiceLocator.getEntityService().register(Projectile); + + } + } diff --git a/source/core/src/test/com/csse3200/game/components/tasks/TNTTowerCombatTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/TNTTowerCombatTaskTest.java new file mode 100644 index 000000000..951185ab0 --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/TNTTowerCombatTaskTest.java @@ -0,0 +1,118 @@ +package com.csse3200.game.components.tasks; + +import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.*; +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.TowerFactory; +import com.csse3200.game.events.EventHandler; +import com.csse3200.game.events.listeners.EventListener0; +import com.csse3200.game.physics.PhysicsEngine; +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.physics.raycast.RaycastHit; +import com.csse3200.game.services.GameTime; +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; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class TNTTowerCombatTaskTest { + + + + Entity entity; + + TNTTowerCombatTask tntTowerCombatTask; + + @BeforeEach + void setUp() { + GameTime gameTime = mock(GameTime.class); + + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + + tntTowerCombatTask = new TNTTowerCombatTask(2,4); + } + + @Test + public void testStartTriggersDefaultEvent() { + + Entity entity = createTNT(); + + EventListener0 defaultStartListener = mock(EventListener0.class); + entity.getEvents().addListener(TNTTowerCombatTask.DEFAULT, defaultStartListener); + + tntTowerCombatTask.start(); + + verify(defaultStartListener).handle(); + } + + @Test + public void testUpdateTowerStateIdleMode() { + + Entity entity = createTNT(); + entity.setPosition(10,10); + + Entity Target = createNPC(); + Target.setPosition(12,10); + + EventListener0 defaultStartListener = mock(EventListener0.class); + // still in idle + assertEquals(TNTTowerCombatTask.STATE.IDLE, tntTowerCombatTask.getState()); + entity.getEvents().addListener(TNTTowerCombatTask.DIG, defaultStartListener); + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + assertTrue(tntTowerCombatTask.isTargetVisible()); + + tntTowerCombatTask.updateTowerState(); + + verify(defaultStartListener).handle(); + // ready to explode + assertEquals(TNTTowerCombatTask.STATE.EXPLODE, tntTowerCombatTask.getState()); + } + + @Test + public void testGetPriority() { + // Arrange + tntTowerCombatTask.readToDelete = false; + + // Act + int priority = tntTowerCombatTask.getPriority(); + + // Assert + + assertEquals(2, priority); + } + + Entity createTNT() { + AITaskComponent aiTaskComponent = new AITaskComponent().addTask(tntTowerCombatTask); + Entity entity = new Entity().addComponent(aiTaskComponent). + addComponent(new PhysicsComponent()) + .addComponent(new HitboxComponent()) + .addComponent(new ColliderComponent()); + 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; + } + + +} From 373f1538952ab26dfb5574b65207f3cc502217e6 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Sun, 10 Sep 2023 18:52:23 +1000 Subject: [PATCH 2/3] Created JUnit tests for DroidCombatTask and TNTCombatTask --- .../components/tasks/DroidCombatTask.java | 48 +++--- .../components/tasks/TNTTowerCombatTask.java | 4 +- .../components/tasks/DroidCombatTaskTest.java | 148 ++++++++++++++++++ .../tasks/TNTTowerCombatTaskTest.java | 72 ++++++--- 4 files changed, 226 insertions(+), 46 deletions(-) create mode 100644 source/core/src/test/com/csse3200/game/components/tasks/DroidCombatTaskTest.java 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 57b91f06e..26eb87fa6 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 @@ -24,13 +24,15 @@ public class DroidCombatTask 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 that the tower will detect // the following four constants are the event names that will be triggered in the state machine - private static final String GO_UP = "goUpStart"; - private static final String GO_DOWN = "goDownStart"; - private static final String ATTACK_UP = "attackUpStart"; - private static final String ATTACK_DOWN = "attackDownStart"; - private static final String WALK = "walkStart"; - private static final String DEATH = "deathStart"; - private static final String IDLE = "idleStart"; + public static final String GO_UP = "goUpStart"; + public static final String GO_DOWN = "goDownStart"; + public static final String ATTACK_UP = "attackUpStart"; + public static final String ATTACK_DOWN = "attackDownStart"; + public static final String WALK = "walkStart"; + public static final String DEATH = "deathStart"; + public static final String IDLE = "idleStart"; + public static final String SHOOT_UP = "ShootUp"; + public static final String SHOOT_DOWN = "ShootDown"; // class attributes @@ -43,10 +45,10 @@ public class DroidCombatTask extends DefaultTask implements PriorityTask { private long endTime; private final RaycastHit hit = new RaycastHit(); - private enum STATE { + public enum STATE { IDLE, UP, DOWN, SHOOT_UP, SHOOT_DOWN, WALK, DIE } - private STATE towerState = STATE.WALK; + public STATE towerState = STATE.WALK; /** * @param priority Task priority when targets are detected (0 when nothing detected). Must be a positive integer. @@ -105,6 +107,7 @@ public void updateTowerState() { case IDLE -> { if (isTargetVisible()) { owner.getEntity().getEvents().trigger(ATTACK_UP); + owner.getEntity().getEvents().trigger(SHOOT_UP); towerState = STATE.DOWN; } else { owner.getEntity().getEvents().trigger(IDLE); @@ -113,12 +116,7 @@ public void updateTowerState() { case SHOOT_DOWN -> { if (isTargetVisible()) { owner.getEntity().getEvents().trigger(ATTACK_DOWN); - Entity Projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100, - owner.getEntity().getPosition().y), new Vector2(2,2), ProjectileEffects.SLOW, false); - Projectile.setScale(new Vector2(0.5f,0.5f)); - Projectile.setPosition((float) (owner.getEntity().getPosition().x + 0.2), - (float) (owner.getEntity().getPosition().y - 0.2)); - ServiceLocator.getEntityService().register(Projectile); + owner.getEntity().getEvents().trigger(SHOOT_DOWN); towerState = STATE.UP; } else { owner.getEntity().getEvents().trigger(GO_UP); @@ -129,13 +127,8 @@ public void updateTowerState() { if (isTargetVisible()) { owner.getEntity().getEvents().trigger(ATTACK_UP); + owner.getEntity().getEvents().trigger(SHOOT_UP); towerState = STATE.DOWN; - Entity Projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100, - owner.getEntity().getPosition().y), new Vector2(2,2), ProjectileEffects.SLOW, false); - Projectile.setScale(new Vector2(0.5f,0.5f)); - Projectile.setPosition((float) (owner.getEntity().getPosition().x + 0.2), - (float) (owner.getEntity().getPosition().y + 0.5)); - ServiceLocator.getEntityService().register(Projectile); } else { owner.getEntity().getEvents().trigger(IDLE); towerState = STATE.IDLE; @@ -176,6 +169,15 @@ public void stop() { // owner.getEntity().getEvents().trigger(STOW); } + /** + * Returns the current state of the tower. + * + * @return the current state of the tower. + */ + public STATE getState() { + return this.towerState; + } + /** * Returns the current priority of the task. * @return active priority value if targets detected, inactive priority otherwise @@ -189,8 +191,10 @@ public int getPriority() { * Uses a raycast to determine whether there are any targets in detection range * @return true if a target is visible, false otherwise */ - private boolean isTargetVisible() { + public boolean isTargetVisible() { // If there is an obstacle in the path to the max range point, mobs visible. return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); } + + } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java index a59361481..2076eb69d 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java @@ -171,10 +171,12 @@ public boolean isTargetVisible() { return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); } - private boolean isReadyToDelete() { + public boolean isReadyToDelete() { return readToDelete; } + + } diff --git a/source/core/src/test/com/csse3200/game/components/tasks/DroidCombatTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/DroidCombatTaskTest.java new file mode 100644 index 000000000..f03282584 --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/DroidCombatTaskTest.java @@ -0,0 +1,148 @@ +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 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 DroidCombatTaskTest { + DroidCombatTask droidCombatTask; + + @BeforeEach + void setUp() { + GameTime gameTime = mock(GameTime.class); + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + ServiceLocator.registerEntityService(new EntityService()); + droidCombatTask = new DroidCombatTask(1, 4); + } + + @Test + public void testStartTriggersWalkEvent() { + Entity entity = createDroid(); + EventListener0 walkListener = mock(EventListener0.class); + // Deploy Droid in the walking state + entity.getEvents().addListener(DroidCombatTask.WALK, walkListener); + droidCombatTask.start(); + verify(walkListener).handle(); + } + + @Test + public void testUpdateTowerStateWithTargetInRange() { + Entity entity = createDroid(); + entity.setPosition(10,10); + + Entity Target = createNPC(); + Target.setPosition(12,10); + + EventListener0 attackUp = mock(EventListener0.class); + EventListener0 attackDown = mock(EventListener0.class); + EventListener0 switchDown = mock(EventListener0.class); + EventListener0 shootUp = mock(EventListener0.class); + EventListener0 shootDown = mock(EventListener0.class); + entity.getEvents().addListener(DroidCombatTask.ATTACK_UP, attackUp); + entity.getEvents().addListener(DroidCombatTask.SHOOT_UP,shootUp); + entity.getEvents().addListener(DroidCombatTask.ATTACK_DOWN, attackDown); + entity.getEvents().addListener(DroidCombatTask.GO_DOWN,switchDown); + entity.getEvents().addListener(DroidCombatTask.SHOOT_DOWN,shootDown); + //Jump to IDLE state + droidCombatTask.start(); + droidCombatTask.towerState = DroidCombatTask.STATE.IDLE; + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + + assertTrue(droidCombatTask.isTargetVisible()); + + droidCombatTask.updateTowerState(); + // By default, Droid aims from top, so shoot from top + verify(attackUp).handle(); + // shoot projectiles from top + verify(shootUp).handle(); + assertEquals(DroidCombatTask.STATE.DOWN, droidCombatTask.getState()); + + droidCombatTask.updateTowerState(); + // switch to aim downwards + verify(switchDown).handle(); + assertEquals(DroidCombatTask.STATE.SHOOT_DOWN, droidCombatTask.getState()); + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + // check if the target is still there to shoot from below + assertTrue(droidCombatTask.isTargetVisible()); + + droidCombatTask.updateTowerState(); + // Shoot from below + verify(attackDown).handle(); + //shoot projectiles from below + verify(shootUp).handle(); + // switch back to aim from top + assertEquals(DroidCombatTask.STATE.UP, droidCombatTask.getState()); + } + + @Test + public void testUpdateTowerStateWithTargetNotInRange() { + Entity entity = createDroid(); + entity.setPosition(10, 10); + + Entity Target = createNPC(); + Target.setPosition(15, 10); + + EventListener0 idle = mock(EventListener0.class); + EventListener0 attackUp = mock(EventListener0.class); + entity.getEvents().addListener(DroidCombatTask.IDLE, idle); + entity.getEvents().addListener(DroidCombatTask.ATTACK_UP,attackUp); + //Jump to IDLE state + droidCombatTask.towerState = DroidCombatTask.STATE.IDLE; + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + // Target out of range + assertFalse(droidCombatTask.isTargetVisible()); + + droidCombatTask.updateTowerState(); + // Droid will remain in Idle and will not shoot + verify(idle).handle(); + verifyNoInteractions(attackUp); + assertEquals(DroidCombatTask.STATE.IDLE, droidCombatTask.getState()); + + } + + + Entity createDroid() { + AITaskComponent aiTaskComponent = new AITaskComponent().addTask(droidCombatTask); + 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/TNTTowerCombatTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/TNTTowerCombatTaskTest.java index 951185ab0..65a0e4724 100644 --- a/source/core/src/test/com/csse3200/game/components/tasks/TNTTowerCombatTaskTest.java +++ b/source/core/src/test/com/csse3200/game/components/tasks/TNTTowerCombatTaskTest.java @@ -2,35 +2,26 @@ import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; -import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.entities.Entity; -import com.csse3200.game.entities.factories.TowerFactory; -import com.csse3200.game.events.EventHandler; +import com.csse3200.game.entities.EntityService; import com.csse3200.game.events.listeners.EventListener0; -import com.csse3200.game.physics.PhysicsEngine; 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.physics.raycast.RaycastHit; import com.csse3200.game.services.GameTime; 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; -import org.mockito.InjectMocks; -import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) public class TNTTowerCombatTaskTest { - - Entity entity; - TNTTowerCombatTask tntTowerCombatTask; @BeforeEach @@ -39,6 +30,7 @@ void setUp() { ServiceLocator.registerTimeSource(gameTime); ServiceLocator.registerPhysicsService(new PhysicsService()); + ServiceLocator.registerEntityService(new EntityService()); tntTowerCombatTask = new TNTTowerCombatTask(2,4); } @@ -57,7 +49,7 @@ public void testStartTriggersDefaultEvent() { } @Test - public void testUpdateTowerStateIdleMode() { + public void testUpdateTowerStateWithTargetInRange() { Entity entity = createTNT(); entity.setPosition(10,10); @@ -65,33 +57,67 @@ public void testUpdateTowerStateIdleMode() { Entity Target = createNPC(); Target.setPosition(12,10); - EventListener0 defaultStartListener = mock(EventListener0.class); + EventListener0 dig = mock(EventListener0.class); + EventListener0 explode = mock(EventListener0.class); + EventListener0 damage = mock(EventListener0.class); // still in idle assertEquals(TNTTowerCombatTask.STATE.IDLE, tntTowerCombatTask.getState()); - entity.getEvents().addListener(TNTTowerCombatTask.DIG, defaultStartListener); + entity.getEvents().addListener(TNTTowerCombatTask.DIG, dig); + entity.getEvents().addListener(TNTTowerCombatTask.EXPLOSION,explode); + entity.getEvents().addListener(TNTTowerCombatTask.DAMAGE,damage); ServiceLocator.getPhysicsService().getPhysics().update(); entity.update(); + // TNT saw the target assertTrue(tntTowerCombatTask.isTargetVisible()); tntTowerCombatTask.updateTowerState(); - - verify(defaultStartListener).handle(); - // ready to explode + // TNT just Dug into the ground + verify(dig).handle(); + // READY TO EXPLODE !!! assertEquals(TNTTowerCombatTask.STATE.EXPLODE, tntTowerCombatTask.getState()); + + tntTowerCombatTask.updateTowerState(); + + // BOOOOOOOOM !! + verify(explode).handle(); + // Apply Damage and Knock-back to Target + verify(damage).handle(); + + // Ready to dispose TNT + assertEquals(TNTTowerCombatTask.STATE.REMOVE, tntTowerCombatTask.getState()); + + tntTowerCombatTask.updateTowerState(); + // Set flag to dispose + assertTrue(tntTowerCombatTask.isReadyToDelete()); + } @Test - public void testGetPriority() { - // Arrange - tntTowerCombatTask.readToDelete = false; + public void testStayAtIdleWhenNoTargetInRange() { - // Act - int priority = tntTowerCombatTask.getPriority(); + Entity entity = createTNT(); + entity.setPosition(10,10); - // Assert + Entity Target = createNPC(); + Target.setPosition(15,10); + + EventListener0 defaultStartListener = mock(EventListener0.class); + // still in idle + assertEquals(TNTTowerCombatTask.STATE.IDLE, tntTowerCombatTask.getState()); + entity.getEvents().addListener(TNTTowerCombatTask.DIG, defaultStartListener); + + ServiceLocator.getPhysicsService().getPhysics().update(); + entity.update(); + // Target not in range + assertFalse(tntTowerCombatTask.isTargetVisible()); + + tntTowerCombatTask.updateTowerState(); + + verifyNoInteractions(defaultStartListener); + // still in idle + assertEquals(TNTTowerCombatTask.STATE.IDLE, tntTowerCombatTask.getState()); - assertEquals(2, priority); } Entity createTNT() { From d61f0745fcb4442900d13c48be5da38726ac05ac Mon Sep 17 00:00:00 2001 From: Mohamad Date: Mon, 11 Sep 2023 02:48:01 +1000 Subject: [PATCH 3/3] fixed some code smells --- .../components/tasks/DroidCombatTask.java | 4 ---- .../components/tasks/TNTTowerCombatTask.java | 20 ------------------- .../components/tower/TNTDamageComponent.java | 8 +------- 3 files changed, 1 insertion(+), 31 deletions(-) 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 26eb87fa6..864f5a3f3 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 @@ -4,9 +4,6 @@ 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; @@ -166,7 +163,6 @@ public void updateTowerState() { @Override public void stop() { super.stop(); -// owner.getEntity().getEvents().trigger(STOW); } /** diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java index 2076eb69d..bcb210846 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TNTTowerCombatTask.java @@ -4,8 +4,6 @@ import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.DefaultTask; import com.csse3200.game.ai.tasks.PriorityTask; -import com.csse3200.game.entities.Entity; -import com.csse3200.game.entities.EntityService; import com.csse3200.game.physics.PhysicsEngine; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.raycast.RaycastHit; @@ -144,24 +142,6 @@ public STATE getState() { return this.towerState; } - /** - * Fetches the active priority of the Task if a target is visible. - * @return (int) active priority if a target is visible, -1 otherwise - */ - private int getActivePriority() { - - return !isTargetVisible() ? 0 : priority; - } - - /** - * Fetches the inactive priority of the Task if a target is not visible. - * @return (int) -1 if a target is not visible, active priority otherwise - */ - private int getInactivePriority() { - - return isTargetVisible() ? priority : 0; - } - /** * Uses a raycast to determine whether there are any targets in detection range * @return true if a target is visible, false otherwise diff --git a/source/core/src/main/com/csse3200/game/components/tower/TNTDamageComponent.java b/source/core/src/main/com/csse3200/game/components/tower/TNTDamageComponent.java index c6acb3c28..ece920971 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/TNTDamageComponent.java +++ b/source/core/src/main/com/csse3200/game/components/tower/TNTDamageComponent.java @@ -24,7 +24,6 @@ * Utilizes HitboxComponent and CombatStatsComponent for functionality. */ public class TNTDamageComponent extends Component { - private static final Logger logger = LoggerFactory.getLogger(TNTDamageComponent.class); private short targetLayer; private float knockbackForce = 0f; private float radius; @@ -84,12 +83,7 @@ private void applyTNTDamage() { // Check for null components and log specifics if (sourceHitbox == null || otherHitbox == null) { - if (sourceHitbox == null) { - logger.debug("Warning: Source Entity without HitboxComponent. Source Entity: " + entity); - } - if (otherHitbox == null) { - logger.debug("Warning: Other Entity without HitboxComponent. Other Entity: " + otherEntity); - } + continue; }