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 e36ca8df7..748b3721f 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -4,9 +4,11 @@ import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.components.CombatStatsComponent; 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.player.PlayerStatsDisplay; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.*; import com.csse3200.game.physics.PhysicsLayer; @@ -30,6 +32,8 @@ public class ForestGameArea extends GameArea { private static final Logger logger = LoggerFactory.getLogger(ForestGameArea.class); + // Counts the number of humans left, if this reaches zero, game over. + private int endStateCounter = 2; private static final int NUM_BUILDINGS = 4; private static final int NUM_WALLS = 7; @@ -40,10 +44,10 @@ public class ForestGameArea extends GameArea { 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(1, 4); @@ -185,6 +189,8 @@ public void create() { displayUI(); spawnTerrain(); + // Set up infrastructure for end game tracking + player = spawnPlayer(); player.getEvents().addListener("spawnWave", this::spawnXenoGrunts); @@ -205,9 +211,9 @@ public void create() { spawnWeaponTower(); spawnTNTTower(); spawnDroidTower(); - spawnEngineer(); + spawnGapScanners(); spawnIncome(); - bossKing1 = spawnBossKing1(); +// bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); @@ -308,7 +314,7 @@ private void spawnGhosts() { private Entity spawnBossKing1() { GridPoint2 minPos = new GridPoint2(0, 0); GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); - GridPoint2 randomPos + GridPoint2 randomPos = new GridPoint2(0, 0); Entity ghostKing = NPCFactory.createGhostKing(player); spawnEntityAt(ghostKing, randomPos, true, true); @@ -612,12 +618,30 @@ private void spawnIncome() { spawnEntityAt(towerfactory, randomPos, 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); + 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 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/player/HumanAnimationController.java b/source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java index e65fc8763..c6231f29e 100644 --- a/source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java @@ -35,10 +35,10 @@ public class HumanAnimationController extends Component { private static final String FIRE_AUTO_SFX = "sounds/engineers/firing_auto.mp3"; private static final String FIRE_SINGLE_SFX = "sounds/engineers/firing_single.mp3"; - AnimationRenderComponent animator; - Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( + private AnimationRenderComponent animator; + private final Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( FIRE_AUTO_SFX, Sound.class); - Sound fireSingleSound = ServiceLocator.getResourceService().getAsset( + private final Sound fireSingleSound = ServiceLocator.getResourceService().getAsset( FIRE_SINGLE_SFX, Sound.class); /** @@ -56,7 +56,7 @@ public void create() { entity.getEvents().addListener(PREP, this::animatePrep); entity.getEvents().addListener(WALK_PREP, this::animatePrepWalk); entity.getEvents().addListener(FIRING_SINGLE, this::animateSingleFiring); - entity.getEvents().addListener(FIRING_AUTO, this::animateFiring); + entity.getEvents().addListener(FIRING_AUTO, this::animateFiringAuto); entity.getEvents().addListener(HIT, this::animateHit); entity.getEvents().addListener(DEATH, this::animateDeath); } @@ -110,7 +110,7 @@ void animateSingleFiring() { * Callback that starts the shoot animation in auto mode and plays the auto fire sound. * Currently unused, but intended to be incorporated as engineer functionality expands. */ - void animateFiring() { + void animateFiringAuto() { animator.startAnimation(FIRE_AUTO_ANIM); fireAutoSound.play(); } 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 21d587be9..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; @@ -24,13 +21,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 +42,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 +104,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 +113,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.45f, 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 +124,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; @@ -173,7 +163,15 @@ public void updateTowerState() { @Override public void stop() { super.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; } /** @@ -189,8 +187,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 c55a92450..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; @@ -22,10 +20,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 +35,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; @@ -136,36 +134,29 @@ public int getPriority() { } /** - * Fetches the active priority of the Task if a target is visible. - * @return (int) active priority if a target is visible, -1 otherwise + * Returns the current state of the tower. + * + * @return the current state of the tower. */ - 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; + public STATE getState() { + return this.towerState; } /** * 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); } - private boolean isReadyToDelete() { + public boolean isReadyToDelete() { return readToDelete; } + + } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/human/HumanMovementTask.java b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanMovementTask.java index 6e957e311..2f647426d 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/human/HumanMovementTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanMovementTask.java @@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory; /** - * Move to a given position, finishing when you get close enough. Requires an entity with a + * Move a human entity to a given position, finishing when you get close enough. Requires an entity with a * PhysicsMovementComponent. */ public class HumanMovementTask extends DefaultTask { diff --git a/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java index a9b497130..b801379ca 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java @@ -13,46 +13,59 @@ import org.slf4j.LoggerFactory; /** - * Wander around by moving a random position within a range of the starting position. Wait a little - * bit between movements. Requires an entity with a PhysicsMovementComponent. + * HumanWanderTask is the entry point for the engineer entity's behaviour. Instantiates subtasks HumanWaitTask, + * HumanMovementTask and EngineerCombatTask, and manages transitions between the tasks. Engineer damage and death + * handled in this class. */ public class HumanWanderTask extends DefaultTask implements PriorityTask { private static final Logger logger = LoggerFactory.getLogger(HumanWanderTask.class); + private static final int TOLERANCE = 1; + private static final float STOP_DISTANCE = 0.5f; + private static final int DEFAULT_PRIORITY = 1; + private static final String DEATH_EVENT = "deathStart"; + private static final String IDLE_EVENT = "idleRight"; - private float maxRange; - private Vector2 wanderRange; + private final float maxRange; private final float waitTime; private Vector2 startPos; private HumanMovementTask movementTask; private HumanWaitTask waitTask; - private EngineerCombatTask combatTask; private Task currentTask; - private boolean isDead = false; /** + * Constructor of HumanWanderTask + * * @param waitTime How long in seconds to wait between wandering. + * @param maxRange Maximum detection and fighting range of the entity */ public HumanWanderTask(float waitTime, float maxRange) { this.waitTime = waitTime; this.maxRange = maxRange; } + /** + * Fetches the priority of this task. + * @return current priority of this task. Priority for this task is a set value and does not change. + */ @Override public int getPriority() { - return 1; // Low priority task + return DEFAULT_PRIORITY; // Low priority task } + /** + * Starts the HumanWanderTask instance and instantiates subtasks (HumanWaitTask, HumanWanderTask, EngineerCombatTask). + * + */ @Override public void start() { super.start(); - startPos = owner.getEntity().getPosition(); - this.wanderRange = owner.getEntity().getCenterPosition(); + this.startPos = owner.getEntity().getCenterPosition(); waitTask = new HumanWaitTask(waitTime); waitTask.create(owner); - movementTask = new HumanMovementTask(this.wanderRange, 1f); + movementTask = new HumanMovementTask(this.startPos, STOP_DISTANCE); movementTask.create(owner); movementTask.start(); @@ -63,58 +76,92 @@ public void start() { currentTask = movementTask; } + /** + * Operates the main logic of the entity in this task. All calls to switch to particular states are determined during + * the update phase. + * The logical flow is: + * - Check if the entity has died since last update + * - Check if the entity has finished dying + * - If not dead + */ @Override public void update() { // Check if engineer has died since last update if (!isDead && owner.getEntity().getComponent(CombatStatsComponent.class).isDead()) { - owner.getEntity().getEvents().trigger("deathStart"); - owner.getEntity().getComponent(ColliderComponent.class).setLayer(PhysicsLayer.NONE); - owner.getEntity().getComponent(HitboxComponent.class).setLayer(PhysicsLayer.NONE); - currentTask.stop(); - // Add a time delay here to allow animation to play? - isDead = true; + startDying(); } - // Check if engineer has finished dying + + // Check if engineer has finished dying animation else if (isDead && owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { owner.getEntity().setFlagForDelete(true); // TODO: make the appropriate calls to decrement the human count. } - // otherwise doing engineer things + + // otherwise doing engineer things since engineer is alive else if (!isDead) { if (currentTask.getStatus() != Status.ACTIVE) { + // if the engineer is in move state and update has been called, engineer has arrived at destination if (currentTask == movementTask) { startWaiting(); - owner.getEntity().getEvents().trigger("idleRight"); + owner.getEntity().getEvents().trigger(IDLE_EVENT); + } else if (combatTask.isTargetVisible()) { - if (combatTask.fetchTarget().y < owner.getEntity().getCenterPosition().y + 2 && - combatTask.fetchTarget().y > owner.getEntity().getCenterPosition().y - 2) { - startCombat(); - } else { - startMoving(new Vector2(owner.getEntity().getCenterPosition().x, combatTask.fetchTarget().y)); - } + // if the engineer is positioned within the tolerance range of the mob's y position, enter combat state + if (combatTask.fetchTarget().y < owner.getEntity().getCenterPosition().y + TOLERANCE && + combatTask.fetchTarget().y > owner.getEntity().getCenterPosition().y - TOLERANCE) { + startCombat(); + + // move into position for targeting mob + } else { + startMoving(new Vector2(owner.getEntity().getCenterPosition().x, combatTask.fetchTarget().y)); + } } } currentTask.update(); } } + /** + * Handle the dying phase of the entity. Triggers an event to play the appropriate media, + * sets HitBox and Collider components to ignore contact (stops the body being pushed around) + * and stops the current task. + */ + private void startDying() { + owner.getEntity().getEvents().trigger(DEATH_EVENT); + owner.getEntity().getComponent(ColliderComponent.class).setLayer(PhysicsLayer.NONE); + owner.getEntity().getComponent(HitboxComponent.class).setLayer(PhysicsLayer.NONE); + currentTask.stop(); + isDead = true; + } + + /** + * Starts the wait task. + */ private void startWaiting() { - logger.debug("Starting waiting"); swapTask(waitTask); } + /** + * Starts the movement task, to a particular destination + * @param destination the Vector2 position to which the entity needs to move + */ private void startMoving(Vector2 destination) { - logger.debug("Starting moving"); movementTask.setTarget(destination); swapTask(movementTask); } + /** + * Starts the combat task. + */ private void startCombat() { - logger.debug("Starting Combat"); swapTask(combatTask); } + /** + * Allows manual switching of tasks, from the current task to the supplied newTask. + * @param newTask the task being switched to. + */ private void swapTask(Task newTask) { if (currentTask != null) { currentTask.stop(); @@ -123,9 +170,11 @@ private void swapTask(Task newTask) { currentTask.start(); } - private Vector2 getDirection() { -// float y = startPos.y; -// return new Vector2(0, y); - return this.wanderRange; + /** + * Fetch the start position. + * @return a Vector2 start position + */ + public Vector2 getStartPos() { + return this.startPos; } } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/scanner/ScannerTask.java b/source/core/src/main/com/csse3200/game/components/tasks/scanner/ScannerTask.java new file mode 100644 index 000000000..3405cecd0 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/scanner/ScannerTask.java @@ -0,0 +1,115 @@ +package com.csse3200.game.components.tasks.scanner; + +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.factories.EngineerFactory; +import com.csse3200.game.physics.PhysicsEngine; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.raycast.RaycastHit; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; + +/** + * ScannerTask implements the behaviour of GapScannerEntities that detect the + * conditions to trigger engineer spawning, i.e., No towers, no engineers, mobs within + * a certain distance. + */ +public class ScannerTask extends DefaultTask implements PriorityTask { + + private static final int SCAN_INTERVAL = 1000; // how often to scan, in milliseconds + private final PhysicsEngine physics; + private final GameTime timeSource; + private final RaycastHit hit = new RaycastHit(); + private Vector2 selfPosition; + private long endTime; + + // booleans to track presence of towers, engineers and mobs + private boolean towers = false; + private boolean engineers = false; + private boolean mobs = false; + + // track the number of engineers spawned. + private static final int maxEngineers = 3; + private int engineerCount = 0; + + /** + * ScannerTask Constructor + */ + public ScannerTask() { + physics = ServiceLocator.getPhysicsService().getPhysics(); + timeSource = ServiceLocator.getTimeSource(); + } + + /** + * Start method for the ScannerTask + */ + @Override + public void start() { + super.start(); + endTime = timeSource.getTime() + (SCAN_INTERVAL); + selfPosition = owner.getEntity().getCenterPosition(); + } + + /** + * Update method for the scanner task. Implements the scanning and spawning logic + * for populating the game area with engineers. + */ + @Override + public void update() { + if (timeSource.getTime() >= endTime) { + // clear all presence booleans + towers = false; + engineers = false; + mobs = false; + + // carry out scan and behave accordingly + scan(); + if (!towers && !engineers && mobs) { + // spawn engineers now + if (engineerCount < maxEngineers) { + Entity engineer = EngineerFactory.createEngineer(); + + engineer.setPosition(new Vector2((int)(selfPosition.x + 1),(int) selfPosition.y)); + ServiceLocator.getEntityService().register(engineer); + engineerCount += 1; + } + } + endTime = timeSource.getTime() + SCAN_INTERVAL; + } + } + + /** + * Scanning method that detects the presence of towers/engineers/mobs. + * Sets the tracking booleans for each of the entity types + */ + private void scan() { + + if (physics.raycast(selfPosition, + new Vector2(selfPosition.x + 10, selfPosition.y), + PhysicsLayer.TOWER, + hit)) { + towers = true; + } else if (physics.raycast(selfPosition, + new Vector2(selfPosition.x + 10, selfPosition.y), + PhysicsLayer.ENGINEER, + hit)) { + engineers = true; + } else if (physics.raycast(selfPosition, + new Vector2(selfPosition.x + 10, selfPosition.y), + PhysicsLayer.NPC, + hit)) { + mobs = true; + } + } + + /** + * Return the priority of the task. + * @return the default priority of this task (a fixed value - no other tasks to run) + */ + @Override + public int getPriority() { + return 1; + } +} 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/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; } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/GapScannerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/GapScannerFactory.java new file mode 100644 index 000000000..553747246 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/factories/GapScannerFactory.java @@ -0,0 +1,36 @@ +package com.csse3200.game.entities.factories; + + +import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.components.tasks.scanner.ScannerTask; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.physics.components.PhysicsComponent; + +/** + * Factory to create scanner entities that determine whether to spawn engineer entities. + * These do not interact with any of the entities in the game area except to detect other entities + * + */ +public class GapScannerFactory { + + /** + * Creates a scanner entity + * @return scanner entity + */ + public static Entity createScanner() { + Entity scanner = new Entity(); + + AITaskComponent aiComponent = new AITaskComponent(); + + scanner + .addComponent(new PhysicsComponent()) + .addComponent(aiComponent); + + scanner.getComponent(AITaskComponent.class).addTask(new ScannerTask()); + return scanner; + } + + private GapScannerFactory() { + throw new IllegalStateException("Instantiating static util class"); + } +} diff --git a/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java b/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java index 99019845a..1f8f063d6 100644 --- a/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java +++ b/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java @@ -58,6 +58,7 @@ void setUp() { resourceService.loadSounds(sounds); resourceService.loadAll(); engineer = EngineerFactory.createEngineer(); + engineer.create(); } @AfterEach @@ -70,46 +71,45 @@ void shouldHaveAnimationController() { "Created Engineer entity should have a HumanAnimationController"); } -// @Test -// void shouldAnimateIdleRight() { -// engineer.getEvents().trigger("idleStart"); -// when(gameTime.getDeltaTime()).thenReturn(0.1f); -// assertEquals("idle_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), -// "'idleStart' event should trigger 'idle_right' animation'"); -// } -// -// @Test -// void animateLeftWalk() { -// engineer.getEvents().trigger("walkLeftStart"); -// assertEquals("walk_left", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), -// "'walkLeftStart' event should trigger 'walk_left' animation'"); -// } -// -// @Test -// void animateRightWalk() { -// engineer.getEvents().trigger("walkRightStart"); -// assertEquals("walk_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), -// "'walkRightStart' event should trigger 'walk_right' animation'"); -// } -// -// @Test -// void animateFiring() { -// engineer.getEvents().trigger("firingSingleStart"); -// assertEquals("firing_single", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), -// "'firingSingleStart' event should trigger 'firing_single' animation'"); -// } -// -// @Test -// void animateHit() { -// engineer.getEvents().trigger("hitStart"); -// assertEquals("hit", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), -// "'hitStart' event should trigger 'hit' animation'"); -// } -// -// @Test -// void animateDeath() { -// engineer.getEvents().trigger("hitStart"); -// assertEquals("death", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), -// "'deathStart' event should trigger 'death' animation'"); -// } + @Test + void shouldAnimateIdleRight() { + engineer.getEvents().trigger("idleRight"); + assertEquals("idle_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'idleRight' event should trigger 'idle_right' animation'"); + } + + @Test + void shouldAnimateLeftWalk() { + engineer.getEvents().trigger("walkLeftStart"); + assertEquals("walk_left", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'walkLeftStart' event should trigger 'walk_left' animation'"); + } + + @Test + void shouldAnimateRightWalk() { + engineer.getEvents().trigger("walkRightStart"); + assertEquals("walk_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'walkRightStart' event should trigger 'walk_right' animation'"); + } + + @Test + void shoudlAnimateFiring() { + engineer.getEvents().trigger("firingSingleStart"); + assertEquals("firing_single", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'firingSingleStart' event should trigger 'firing_single' animation'"); + } + + @Test + void shouldAnimateHit() { + engineer.getEvents().trigger("hitStart"); + assertEquals("hit", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'hitStart' event should trigger 'hit' animation'"); + } + + @Test + void shouldAnimateDeath() { + engineer.getEvents().trigger("deathStart"); + assertEquals("death", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'deathStart' event should trigger 'death' animation'"); + } } \ No newline at end of file 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 new file mode 100644 index 000000000..65a0e4724 --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/TNTTowerCombatTaskTest.java @@ -0,0 +1,144 @@ +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.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 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 TNTTowerCombatTaskTest { + + + TNTTowerCombatTask tntTowerCombatTask; + + @BeforeEach + void setUp() { + GameTime gameTime = mock(GameTime.class); + + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + ServiceLocator.registerEntityService(new EntityService()); + + 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 testUpdateTowerStateWithTargetInRange() { + + Entity entity = createTNT(); + entity.setPosition(10,10); + + Entity Target = createNPC(); + Target.setPosition(12,10); + + 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, 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(); + // 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 testStayAtIdleWhenNoTargetInRange() { + + Entity entity = createTNT(); + entity.setPosition(10,10); + + 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()); + + } + + 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; + } + + +} diff --git a/source/core/src/test/com/csse3200/game/components/tasks/human/HumanMovementTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/human/HumanMovementTaskTest.java deleted file mode 100644 index 4deb83386..000000000 --- a/source/core/src/test/com/csse3200/game/components/tasks/human/HumanMovementTaskTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.csse3200.game.components.tasks.human; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class HumanMovementTaskTest { - - @BeforeEach - void setUp() { - } - - @AfterEach - void tearDown() { - } - - @Test - void start() { - } - - @Test - void update() { - } - - @Test - void setTarget() { - } - - @Test - void stop() { - } -} \ No newline at end of file diff --git a/source/core/src/test/com/csse3200/game/components/tasks/human/HumanWanderTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/human/HumanWanderTaskTest.java index 5a24deb7c..6e7c9eaeb 100644 --- a/source/core/src/test/com/csse3200/game/components/tasks/human/HumanWanderTaskTest.java +++ b/source/core/src/test/com/csse3200/game/components/tasks/human/HumanWanderTaskTest.java @@ -1,30 +1,104 @@ package com.csse3200.game.components.tasks.human; +import com.csse3200.game.components.TouchAttackComponent; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.EngineerFactory; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.physics.components.HitboxComponent; +import com.csse3200.game.physics.components.PhysicsComponent; +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.AfterEach; 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; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +@ExtendWith(GameExtension.class) +@ExtendWith(MockitoExtension.class) class HumanWanderTaskTest { + /** + * Class for testing the HumanWanderTask, adapted from WanderTaskTest by + * Jonathan Tang + */ + + Entity owner; + + private final String[] atlas = {"images/engineers/engineer.atlas"}; + private static final String[] sounds = { + "sounds/engineers/firing_auto.mp3", + "sounds/engineers/firing_single.mp3" + }; @BeforeEach void setUp() { + GameTime gameTime = new GameTime(); + PhysicsService physics = new PhysicsService(); + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(physics); + RenderService render = new RenderService(); + render.setDebug(mock(DebugRenderer.class)); + ServiceLocator.registerRenderService(render); + ResourceService resourceService = new ResourceService(); + ServiceLocator.registerResourceService(resourceService); + resourceService.loadTextureAtlases(atlas); + resourceService.loadSounds(sounds); + resourceService.loadAll(); + owner = EngineerFactory.createEngineer(); + owner.create(); } @AfterEach void tearDown() { } + @Test + void start() { + + } + @Test void getPriority() { } @Test - void start() { + void shouldStartWaiting() { + + } + + @Test + void shouldStartMoving() { + + } + + @Test + void shouldStartCombat() { + + } + + @Test + void shouldSwapTask() { + } @Test void update() { } + + Entity createEnemy() { + Entity enemy = mock(Entity.class); + enemy + .addComponent(new HitboxComponent().setLayer(PhysicsLayer.NPC)) + .addComponent(new PhysicsComponent()) + .addComponent(new TouchAttackComponent(PhysicsLayer.ENGINEER)); + return enemy; + } } \ No newline at end of file diff --git a/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java b/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java index 3d80accb0..d130ae5c7 100644 --- a/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java +++ b/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java @@ -55,7 +55,7 @@ class EngineerFactoryTest { "firing_single", "hit", "death" - };; + }; @BeforeEach void setUp() { diff --git a/source/wiki/team-2/EngineerFactory Sequence Diagram.png b/source/wiki/team-2/EngineerFactory Sequence Diagram.png new file mode 100644 index 000000000..b8ec04420 Binary files /dev/null and b/source/wiki/team-2/EngineerFactory Sequence Diagram.png differ diff --git a/source/wiki/team-2/EngineerFactory Sequence Diagram.svg b/source/wiki/team-2/EngineerFactory Sequence Diagram.svg new file mode 100644 index 000000000..42ee4e04b --- /dev/null +++ b/source/wiki/team-2/EngineerFactory Sequence Diagram.svg @@ -0,0 +1,210 @@ + + + diff --git a/source/wiki/team-2/EngineerFactory UML.png b/source/wiki/team-2/EngineerFactory UML.png new file mode 100644 index 000000000..3aa08fefb Binary files /dev/null and b/source/wiki/team-2/EngineerFactory UML.png differ diff --git a/source/wiki/team-2/EngineerFactory and GapScannerFactory Sequence Diagram.png b/source/wiki/team-2/EngineerFactory and GapScannerFactory Sequence Diagram.png new file mode 100644 index 000000000..b8a09a56e Binary files /dev/null and b/source/wiki/team-2/EngineerFactory and GapScannerFactory Sequence Diagram.png differ diff --git a/source/wiki/team-2/HumanWanderTask Sequence Diagram.png b/source/wiki/team-2/HumanWanderTask Sequence Diagram.png new file mode 100644 index 000000000..c23337d2e Binary files /dev/null and b/source/wiki/team-2/HumanWanderTask Sequence Diagram.png differ