From c42b661dc91501e176d0f08d3c9eea37c6927430 Mon Sep 17 00:00:00 2001 From: Vishal-jodd Date: Tue, 3 Oct 2023 06:10:00 +1000 Subject: [PATCH 1/4] Added the combat task for the bombship --- .../tasks/bombship/BombshipCombatTask.java | 113 ++++++++++++++++++ .../tasks/human/HumanWanderTask.java | 2 +- 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipCombatTask.java diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipCombatTask.java new file mode 100644 index 000000000..7f02204be --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipCombatTask.java @@ -0,0 +1,113 @@ +package com.csse3200.game.components.tasks; + +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.ai.tasks.Task; +import com.csse3200.game.components.tasks.MovementTask; +import com.csse3200.game.components.tasks.WaitTask; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.ProjectileFactory; +import com.csse3200.game.physics.PhysicsEngine; +import com.csse3200.game.physics.raycast.RaycastHit; +import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.physics.PhysicsLayer; +import org.slf4j.Logger; +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. + */ +public class BombshipCombatTask extends DefaultTask implements PriorityTask { + private static final Logger logger = LoggerFactory.getLogger(BombshipCombatTask.class); + + private final float waitTime; + private Vector2 currentPos; + private MovementTask movementTask; + private WaitTask waitTask; + private Task currentTask; + /** + * Animation event names + */ + private static final String IDLE = "idle"; + private static final String START = "start"; + private static final String DESTROY = "destroy"; + + private enum STATE { + IDLE, START, DESTROY + } + + private PhysicsEngine physics; + private static final short TARGET = PhysicsLayer.TOWER; + private final RaycastHit hit = new RaycastHit(); + + /** + * @param waitTime How long in seconds to wait between wandering. + */ + public BombshipCombatTask(float waitTime) { + this.waitTime = waitTime; + physics = ServiceLocator.getPhysicsService().getPhysics(); + } + + @Override + public int getPriority() { + return 1; // Low priority task + } + + @Override + public void start() { + super.start(); + currentPos = owner.getEntity().getPosition(); + + waitTask = new WaitTask(waitTime); + waitTask.create(owner); + movementTask = new MovementTask(currentPos.sub(2, 0)); + movementTask.create(owner); + + movementTask.start(); + owner.getEntity().getEvents().trigger("idle"); + currentTask = movementTask; + + this.owner.getEntity().getEvents().trigger("start"); + } + + @Override + public void update() { + if (currentTask.getStatus() != Status.ACTIVE) { + if (currentTask != movementTask) { + if (isEngineerDied()) { + owner.getEntity().getEvents().trigger("start"); + this.owner.getEntity().getEvents().trigger("destroy"); + } + owner.getEntity().getEvents().trigger(START); + startWaiting(); + } else { + startMoving(); + } + } + currentTask.update(); + } + + private void startWaiting() { + logger.debug("Starting waiting"); + owner.getEntity().getEvents().trigger("idle"); + swapTask(waitTask); + } + + private void startMoving() { + logger.debug("Starting moving"); + owner.getEntity().getEvents().trigger("start"); + owner.getEntity().getEvents().trigger("destroy"); + movementTask.setTarget(currentPos.sub(2, 0)); + swapTask(movementTask); + } + + private void swapTask(Task newTask) { + if (currentTask != null) { + currentTask.stop(); + } + currentTask = newTask; + currentTask.start(); + } +} 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 528348b8a..32e185088 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 @@ -29,7 +29,7 @@ public class HumanWanderTask extends DefaultTask implements PriorityTask { private HumanWaitTask waitTask; private EngineerCombatTask combatTask; private Task currentTask; - private boolean isDead = false; + private boolean isDead = false; private boolean hasDied = false; From 5af5a14d4ac0a5713fcbaef6a19c8786d32f8fd9 Mon Sep 17 00:00:00 2001 From: Vishal-jodd Date: Tue, 3 Oct 2023 07:32:29 +1000 Subject: [PATCH 2/4] Added the Bombship Wander Task --- .../tasks/bombship/BombshipWaitTask.java | 2 +- .../tasks/bombship/BombshipWanderTask.java | 168 ++++++++++++++++++ 2 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWanderTask.java diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWaitTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWaitTask.java index 88002eb27..c126fef03 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWaitTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWaitTask.java @@ -1,4 +1,4 @@ -package com.csse3200.game.components.tasks.tower; +package com.csse3200.game.components.tasks; import com.csse3200.game.ai.tasks.DefaultTask; import com.csse3200.game.services.GameTime; diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWanderTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWanderTask.java new file mode 100644 index 000000000..8817c331b --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWanderTask.java @@ -0,0 +1,168 @@ +package com.csse3200.game.components.tasks; + +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.ai.tasks.Task; +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.components.ColliderComponent; +import com.csse3200.game.physics.components.HitboxComponent; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.services.ServiceLocator; + +/** + * BombshipWanderTask is the entry point for the engineer entity's behaviour. Instantiates subtasks HumanWaitTask, + * BombshipMovementTask and BombshipCombatTask, and manages transitions between the tasks. Bombship damage and destruction is + * handled in this class. + */ +public class BombshipWanderTask extends DefaultTask implements PriorityTask { + 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 START = "start"; + private static final String DESTROY = "destroy"; + private static final String IDLE = "idle"; + private AnimationRenderComponent animator; + private final float maxRange; + private final float waitTime; + private BombshipMovementTask movementTask; + private BombshipWaitTask waitTask; + private BombshipCombatTask combatTask; + private Task currentTask; + private boolean isDestroyed = false; + + /** + * Constructor of BombshipWanderTask + * + * @param waitTime How long in seconds to wait between wandering. + * @param maxRange Maximum of the entity to fight + */ + public BombshipWanderTask(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 DEFAULT_PRIORITY; // Low priority task + } + + /** + * Starts the BombshipWanderTask instance and instantiates subtasks (BombshipWaitTask, BombshipWanderTask, BombshipCombatTask). + */ + @Override + public void start() { + super.start(); + Vector2 startPos = owner.getEntity().getCenterPosition(); + waitTask = new BombshipWaitTask(waitTime); + waitTask.create(owner); + + movementTask = new BombshipMovementTask(startPos, STOP_DISTANCE); + movementTask.create(owner); + movementTask.start(); + + combatTask = new BombshipCombatTask(maxRange); + combatTask.create(owner); + combatTask.start(); + + currentTask = movementTask; + + animator = owner.getEntity().getComponent(AnimationRenderComponent.class); + } + + /** + * 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() { + if (!isDestroyed) { + startDestroying(); + } + + boolean justDied = owner.getEntity().getComponent(CombatStatsComponent.class).isDestroyed(); + // Check if engineer has died since last update + if (!isDestroyed) { + startDestroying(); + } else if (isDestroyed && animator.isFinished()) { + owner.getEntity().setFlagForDelete(true); + } + + // otherwise doing engineer things since engineer is alive + else if (!isDestroyed){ + doBombshipThings(); + + currentTask.update(); + } + } + + private void doBombshipThings() { + 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(IDLE); + + } else if (combatTask.isEngineerDied()) { + owner.getEntity().getEvents().trigger(START); + } + } + } + /** + * 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 startDestroying() { + owner.getEntity().getEvents().trigger(DESTROY); + owner.getEntity().getComponent(ColliderComponent.class).setLayer(PhysicsLayer.NONE); + owner.getEntity().getComponent(HitboxComponent.class).setLayer(PhysicsLayer.NONE); + currentTask.stop(); + isDestroyed = true; + } + + /** + * Starts the wait task. + */ + private void startWaiting() { + 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) { + movementTask.setTarget(destination); + swapTask(movementTask); + } + + /** + * Starts the combat task. + */ + private void startCombat() { + 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(); + } + currentTask = newTask; + currentTask.start(); + } +} From 2e52c800e7db2a2cb5f8a1ee7cdf85dca0f70c6b Mon Sep 17 00:00:00 2001 From: Vishal-jodd Date: Tue, 3 Oct 2023 09:37:11 +1000 Subject: [PATCH 3/4] Added targets to combat task , the targets are boss and mob --- .../BombShipAnimationController.java | 1 - .../tasks/bombship/BombshipCombatTask.java | 197 ++++++++++++------ .../tasks/bombship/BombshipWanderTask.java | 3 +- 3 files changed, 131 insertions(+), 70 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/BombShipAnimationController.java b/source/core/src/main/com/csse3200/game/components/BombShipAnimationController.java index a74a047fc..5fdabc7a7 100644 --- a/source/core/src/main/com/csse3200/game/components/BombShipAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/BombShipAnimationController.java @@ -1,4 +1,3 @@ -//BombshipController package com.csse3200.game.components.npc; import com.csse3200.game.components.Component; diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipCombatTask.java index 7f02204be..349e5434b 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipCombatTask.java @@ -3,111 +3,174 @@ import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.DefaultTask; import com.csse3200.game.ai.tasks.PriorityTask; -import com.csse3200.game.ai.tasks.Task; -import com.csse3200.game.components.tasks.MovementTask; -import com.csse3200.game.components.tasks.WaitTask; 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.services.GameTime; import com.csse3200.game.services.ServiceLocator; -import com.csse3200.game.physics.PhysicsLayer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import java.util.ArrayList; /** - * 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. + * The AI Task for the Engineer entity. The Engineer will scan for targets within its detection range + * and trigger events to change its state accordingly. This task must be called once the Engineer has + * appropiately moved into position. */ public class BombshipCombatTask extends DefaultTask implements PriorityTask { - private static final Logger logger = LoggerFactory.getLogger(BombshipCombatTask.class); - private final float waitTime; - private Vector2 currentPos; - private MovementTask movementTask; - private WaitTask waitTask; - private Task currentTask; - /** - * Animation event names - */ - private static final String IDLE = "idle"; + private static final int INTERVAL = 1; // The time interval for each target scan from the Engineer. + private static final int PRIORITY = 3; // Default priority of the combat task when mobs are in range. + private static final short TARGET1 = PhysicsLayer.BOSS; // The type of targets that the Engineer will detect. + private static final short TARGET2 = PhysicsLayer.XENO; + + // Animation event names for the Engineer's state machine. private static final String START = "start"; + private static final String IDLE = "idle"; private static final String DESTROY = "destroy"; - private enum STATE { - IDLE, START, DESTROY - } + // The Engineer's attributes. + private final float maxRange; // The maximum range of the bombship. + private Vector2 bombShipPosition = new Vector2(0, 0); // Placeholder value for the Engineer's position. + private final Vector2 maxRangePosition = new Vector2(); private PhysicsEngine physics; - private static final short TARGET = PhysicsLayer.TOWER; + private GameTime timeSource; + private long endTime; + private long reloadTime; + + private ArrayList hits = new ArrayList<>(); private final RaycastHit hit = new RaycastHit(); + private ArrayList targets = new ArrayList<>(); - /** - * @param waitTime How long in seconds to wait between wandering. - */ - public BombshipCombatTask(float waitTime) { - this.waitTime = waitTime; - physics = ServiceLocator.getPhysicsService().getPhysics(); + /** The Engineer's states. */ + private enum STATE { + IDLE, START , DESTROY } + private STATE bombshipState = STATE.IDLE; - @Override - public int getPriority() { - return 1; // Low priority task + public BombshipCombatTask(float maxRange) { + this.maxRange = maxRange; + physics = ServiceLocator.getPhysicsService().getPhysics(); + timeSource = ServiceLocator.getTimeSource(); } + /** + * Runs the task and triggers Bombship's idle animation. + */ @Override public void start() { super.start(); - currentPos = owner.getEntity().getPosition(); - - waitTask = new WaitTask(waitTime); - waitTask.create(owner); - movementTask = new MovementTask(currentPos.sub(2, 0)); - movementTask.create(owner); - - movementTask.start(); - owner.getEntity().getEvents().trigger("idle"); - currentTask = movementTask; - - this.owner.getEntity().getEvents().trigger("start"); + this.bombShipPosition = owner.getEntity().getCenterPosition(); + this.maxRangePosition.set(bombShipPosition.x + maxRange, bombShipPosition.y); + // Default to idle mode + owner.getEntity().getEvents().trigger(IDLE); + endTime = timeSource.getTime() + (INTERVAL * 600); } + /** + * The update method is what is run every time the TaskRunner in the AiTaskComponent calls update(). + * triggers events depending on the presence or otherwise of targets in the detection range + */ @Override public void update() { - if (currentTask.getStatus() != Status.ACTIVE) { - if (currentTask != movementTask) { + if (timeSource.getTime() >= endTime) { + updateBombshipState(); + endTime = timeSource.getTime() + (INTERVAL * 1200); + } + } + + /** + * Bombship state machine + */ + public void updateBombshipState() { + // configure engineer state depending on target visibility + switch (bombshipState) { + case IDLE -> { + // targets detected in idle mode - start deployment if (isEngineerDied()) { - owner.getEntity().getEvents().trigger("start"); - this.owner.getEntity().getEvents().trigger("destroy"); + combatState(); } - owner.getEntity().getEvents().trigger(START); - startWaiting(); - } else { - startMoving(); + } + case START -> { + // targets gone - stop firing + if (!isEngineerDied()) { + owner.getEntity().getEvents().trigger(IDLE); + bombshipState = STATE.IDLE; + } else { + owner.getEntity().getEvents().trigger(START); + } + } + case DESTROY -> { + owner.getEntity().getEvents().trigger(DESTROY); } } - currentTask.update(); } - private void startWaiting() { - logger.debug("Starting waiting"); - owner.getEntity().getEvents().trigger("idle"); - swapTask(waitTask); + /** + * Puts the BombshipCombatTask state into combat mode + */ + private void combatState() { + owner.getEntity().getEvents().trigger(START); + bombshipState = STATE.START; + } + /** + * For stopping the running task + */ + @Override + public void stop() { + super.stop(); } - private void startMoving() { - logger.debug("Starting moving"); - owner.getEntity().getEvents().trigger("start"); - owner.getEntity().getEvents().trigger("destroy"); - movementTask.setTarget(currentPos.sub(2, 0)); - swapTask(movementTask); + /** + * Simplified getPriority function, returns the priority of the task + * @return priority as an integer value. If mobs are visible, return the current priority, otherwise return 0. + */ + @Override + public int getPriority() { + return isEngineerDied() ? PRIORITY : 0; } - private void swapTask(Task newTask) { - if (currentTask != null) { - currentTask.stop(); + /** + * Uses a raycast to determine whether there are any targets in detection range. Performs multiple raycasts + * to a range of points at x = engineer.x + maxRange, and a range of y values above and below current y position. + * Allows the bombship entity to detect mobs in adjacent lanes. + * @return true if a target is detected, false otherwise + */ + public boolean isEngineerDied() { + // If there is an obstacle in the path to the max range point, mobs visible. + Vector2 position = owner.getEntity().getCenterPosition(); + hits.clear(); + for (int i = 8; i > -8; i--) { + if (physics.raycast(position, new Vector2(position.x + maxRange, position.y + i), TARGET1, hit) + || physics.raycast(position, new Vector2(position.x + maxRange, position.y + i), TARGET2, hit)) { + hits.add(hit); + targets.add(new Vector2(position.x + maxRange, position.y + i)); + } + } + return !hits.isEmpty(); + } + + /** + * Fetches the nearest target from the array of detected target positions created during the last call of + * isTargetVisible + * @return a Vector2 position of the nearest mob detected. + */ + public Vector2 fetchTarget() { + // Initial nearest position for comparison + int lowest = 10; + + Vector2 nearest = new Vector2(owner.getEntity().getCenterPosition().x, + owner.getEntity().getCenterPosition().y); + + // Find the nearest target from the array of targets + for (Vector2 tgt : targets){ + if (Math.abs(tgt.y - nearest.y) < lowest) { + lowest = (int)Math.abs(tgt.y - nearest.y); + nearest = tgt; + } } - currentTask = newTask; - currentTask.start(); + return nearest; } } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWanderTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWanderTask.java index 8817c331b..8e6ba4a45 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWanderTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/bombship/BombshipWanderTask.java @@ -89,8 +89,7 @@ public void update() { startDestroying(); } - boolean justDied = owner.getEntity().getComponent(CombatStatsComponent.class).isDestroyed(); - // Check if engineer has died since last update + // Check if bombship has destroyed since last update if (!isDestroyed) { startDestroying(); } else if (isDestroyed && animator.isFinished()) { From 915648fa39bffada2117513506cbd6321fbe2833 Mon Sep 17 00:00:00 2001 From: Vishal-jodd Date: Tue, 3 Oct 2023 10:37:43 +1000 Subject: [PATCH 4/4] Added the Config class --- .../BombShipAnimationController.java | 0 .../entities/configs/BombshipConfigs.java | 10 ++ .../entities/factories/BombshipFactory.java | 94 +++++++++++++++++++ .../csse3200/game/physics/PhysicsLayer.java | 1 + 4 files changed, 105 insertions(+) rename source/core/src/main/com/csse3200/game/components/{ => player}/BombShipAnimationController.java (100%) create mode 100644 source/core/src/main/com/csse3200/game/entities/configs/BombshipConfigs.java create mode 100644 source/core/src/main/com/csse3200/game/entities/factories/BombshipFactory.java diff --git a/source/core/src/main/com/csse3200/game/components/BombShipAnimationController.java b/source/core/src/main/com/csse3200/game/components/player/BombShipAnimationController.java similarity index 100% rename from source/core/src/main/com/csse3200/game/components/BombShipAnimationController.java rename to source/core/src/main/com/csse3200/game/components/player/BombShipAnimationController.java diff --git a/source/core/src/main/com/csse3200/game/entities/configs/BombshipConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/BombshipConfigs.java new file mode 100644 index 000000000..4869dbb9b --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/configs/BombshipConfigs.java @@ -0,0 +1,10 @@ +package com.csse3200.game.entities.configs; + +/** + * Defines the properties stored in Bombship config files to be loaded by the Bombship Factory. + */ +public class BombshipConfigs extends BaseEntityConfig { + public BaseEntityConfig bombship = new BaseEntityConfig(); + public int health = 100; + public int baseAttack = 20; +} diff --git a/source/core/src/main/com/csse3200/game/entities/factories/BombshipFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/BombshipFactory.java new file mode 100644 index 000000000..b86ad2f2b --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/factories/BombshipFactory.java @@ -0,0 +1,94 @@ +package com.csse3200.game.entities.factories; + +import com.badlogic.gdx.graphics.g2d.Animation; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.components.TouchAttackComponent; +import com.csse3200.game.components.player.BombShipAnimationController; +import com.csse3200.game.components.tasks.bombship.BombshipWanderTask; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.configs.*; +import com.csse3200.game.files.FileLoader; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.PhysicsUtils; +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.components.PhysicsMovementComponent; +import com.csse3200.game.rendering.AnimationRenderComponent; + +/** + * Factory to create non-playable human character (NPC) entities with predefined components. + * + * These may be modified to become controllable characters in future sprints. + * + *

Each NPC entity type should have a creation method that returns a corresponding entity. + * Predefined entity properties can be loaded from configs stored as json files which are defined in + * "NPCConfigs". + * + *

If needed, this factory can be separated into more specific factories for entities with + * similar characteristics. + */ +public class BombshipFactory { + + private static final int COMBAT_TASK_PRIORITY = 2; + private static final int BOMBSHIP_RANGE = 30; + private static final EngineerConfigs configs = + FileLoader.readClass(EngineerConfigs.class, "configs/Bombship.json"); + + private static final float HUMAN_SCALE_X = 1f; + private static final float HUMAN_SCALE_Y = 0.8f; + + /** + * Creates an Engineer entity, based on a base Human entity, with the appropriate components and animations + * + * + * @return entity + */ + public static Entity createEngineer() { + Entity bombship = createBaseshipNPC(); + BaseEntityConfig config = configs.bombship; + + AnimationRenderComponent animator = new AnimationRenderComponent( + new TextureAtlas("images/engineers/bombship.atlas")); + animator.addAnimation("start", 0.2f, Animation.PlayMode.LOOP); + animator.addAnimation("destroy", 0.1f, Animation.PlayMode.NORMAL); + animator.addAnimation("idle", 0.2f, Animation.PlayMode.LOOP); + AITaskComponent aiComponent = new AITaskComponent(); + + bombship + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + .addComponent(animator) + .addComponent(new BombShipAnimationController()) + .addComponent(aiComponent); + + bombship.getComponent(AITaskComponent.class).addTask(new BombshipWanderTask(COMBAT_TASK_PRIORITY, BOMBSHIP_RANGE)); + bombship.getComponent(AnimationRenderComponent.class).scaleEntity(); + bombship.setScale(HUMAN_SCALE_X, HUMAN_SCALE_Y); + return bombship; + } + + /** + * Creates a generic human npc to be used as a base entity by more specific NPC creation methods. + * + * @return entity + */ + public static Entity createBaseshipNPC() { + + + Entity ship = + new Entity() + .addComponent(new PhysicsComponent()) + .addComponent(new PhysicsMovementComponent()) + .addComponent(new ColliderComponent()) + .addComponent(new HitboxComponent().setLayer(PhysicsLayer.BOMBSHIP)) + .addComponent(new TouchAttackComponent(PhysicsLayer.NPC, 1.5f)); + + return ship; + } + + private BombshipFactory() { + throw new IllegalStateException("Instantiating static util class"); + } +} \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java b/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java index 37d4b64c5..32487b9a8 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java @@ -4,6 +4,7 @@ public class PhysicsLayer { public static final short NONE = 0; public static final short DEFAULT = (1 << 0); public static final short ENGINEER = (1 << 1); + public static final short BOMBSHIP = (1 << 1); // Terrain obstacle, e.g. trees public static final short OBSTACLE = (1 << 2); // NPC (Non-Playable Character) colliders