From b13b1497ea05228c4109a5e8c211360eeb939ff9 Mon Sep 17 00:00:00 2001 From: praneetdhoolia Date: Mon, 4 Sep 2023 16:46:19 +1000 Subject: [PATCH 01/40] Added EngineerConfig with basic attributes for Engineer Entity. --- .../com/csse3200/game/entities/configs/EngineerConfig.java | 6 ++++++ .../csse3200/game/entities/factories/EngineerFactory.java | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/entities/configs/EngineerConfig.java create mode 100644 source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java diff --git a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfig.java b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfig.java new file mode 100644 index 000000000..121fb0b9c --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfig.java @@ -0,0 +1,6 @@ +package com.csse3200.game.entities.configs; +/** Defines the basic set of properties for an Engineer entity to be loaded by EngineerFactory */ +public class EngineerConfig extends BaseEntityConfig { + public int health = 1; + public int baseAttack = 0; +} \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java new file mode 100644 index 000000000..30a1e2d9e --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -0,0 +1,4 @@ +package com.csse3200.game.entities.factories; + +public class EngineerFactory { +} \ No newline at end of file From 0e9fb05a794ddf77b9921ce3a9beb4afe7f44889 Mon Sep 17 00:00:00 2001 From: praneetdhoolia Date: Mon, 4 Sep 2023 16:47:30 +1000 Subject: [PATCH 02/40] Added EngineerConfig for storing basic Engineer entity attributes and EngineerFactory to produce Engineer entities. --- .../com/csse3200/game/entities/factories/EngineerFactory.java | 1 + 1 file changed, 1 insertion(+) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 30a1e2d9e..a11267e06 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -1,4 +1,5 @@ package com.csse3200.game.entities.factories; public class EngineerFactory { + } \ No newline at end of file From c6817e93c2adc7c401d764c9da71500a82c83f11 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Mon, 4 Sep 2023 22:08:48 +1000 Subject: [PATCH 03/40] created skeleton engineer animation controller class --- .../player/HumanAnimationController.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java 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 new file mode 100644 index 000000000..01e91b257 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java @@ -0,0 +1,75 @@ +package com.csse3200.game.components.player; + +import com.badlogic.gdx.audio.Sound; +import com.csse3200.game.components.Component; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.services.ServiceLocator; + +/** + * Listens for events relevant to a Human character (Just engineers at this stage) + * Each event will trigger a certain animation + */ +public class HumanAnimationController extends Component { + // Event name constants + private static final String IDLE = "idleStart"; + private static final String RUN = "runStart"; + private static final String FIRING = "firingStart"; + private static final String HIT = "hitStart"; + private static final String DEATH = "deathStart"; + // Animation name constants + private static final String IDLE_ANIM = "idle"; + private static final String RUN_ANIM = "run"; + private static final String FIRE_ANIM = "firing"; + private static final String HIT_ANIM = "hit"; + private static final String DEATH_ANIM = "death"; + // Sound effects constants +// private static final String RUN_SFX = "run/todeploy.mp3"; +// private static final String FIRE_SFX = "sounds/gun_shot_trimmed.mp3"; +// private static final String HIT_SFX = "sounds/stow.mp3"; +// private static final String DEATH_SFX = "sounds/stow.mp3"; + + AnimationRenderComponent animator; +// Sound runSound = ServiceLocator.getResourceService().getAsset( +// RUN_SFX, Sound.class); +// Sound attackSound = ServiceLocator.getResourceService().getAsset( +// FIRE_SFX, Sound.class); +// Sound hitSound = ServiceLocator.getResourceService().getAsset( +// HIT_SFX, Sound.class); +// Sound deathSound = ServiceLocator.getResourceService().getAsset( +// HIT_SFX, Sound.class); + + @Override + public void create() { + super.create(); + animator = this.entity.getComponent(AnimationRenderComponent.class); + entity.getEvents().addListener(IDLE, this::animateIdle); + entity.getEvents().addListener(RUN, this::animateRun); + entity.getEvents().addListener(FIRING, this::animateFiring); + entity.getEvents().addListener(HIT, this::animateHit); + entity.getEvents().addListener(DEATH, this::animateDeath); + } + + void animateIdle() { + animator.startAnimation(IDLE_ANIM); + } + + void animateRun() { + animator.startAnimation(RUN_ANIM); +// runSound.play(); + } + + void animateFiring() { + animator.startAnimation(FIRE_ANIM); +// attackSound.play(); + } + + void animateHit() { + animator.startAnimation(HIT_ANIM); +// hitSound.play(); + } + + void animateDeath() { + animator.startAnimation(DEATH_ANIM); +// deathSound.play(); + } +} \ No newline at end of file From afeb8690f197211dcf98e7ed56c996914aafd5f4 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Tue, 5 Sep 2023 12:17:14 +1000 Subject: [PATCH 04/40] added initial engineer assets --- .../assets/images/engineers/engineer.atlas | 132 ++++++++++++++++++ .../core/assets/images/engineers/engineer.png | Bin 0 -> 6158 bytes .../player/HumanAnimationController.java | 20 ++- 3 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 source/core/assets/images/engineers/engineer.atlas create mode 100644 source/core/assets/images/engineers/engineer.png diff --git a/source/core/assets/images/engineers/engineer.atlas b/source/core/assets/images/engineers/engineer.atlas new file mode 100644 index 000000000..1cfc350ef --- /dev/null +++ b/source/core/assets/images/engineers/engineer.atlas @@ -0,0 +1,132 @@ + +engineer.png +size: 128, 256 +format: RGBA8888 +filter: Nearest, Nearest +repeat: none +idle + rotate: false + xy: 42, 42 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 8 +walk_left + rotate: false + xy: 5, 190 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 6 +walk_left + rotate: false + xy: 5, 116 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 3 +walk_left + rotate: false + xy: 42, 153 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 8 +walk_left + rotate: false + xy: 5, 79 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 5 +walk_left + rotate: false + xy: 5, 42 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 2 +walk_left + rotate: false + xy: 42, 79 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 7 +walk_left + rotate: false + xy: 5, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 4 +walk_left + rotate: false + xy: 42, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 +default + rotate: false + xy: 5, 190 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 6 +walk_right + rotate: false + xy: 5, 153 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 2 +walk_right + rotate: false + xy: 42, 190 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 7 +walk_right + rotate: false + xy: 79, 190 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 4 +walk_right + rotate: false + xy: 42, 116 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 +walk_right + rotate: false + xy: 79, 153 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 6 +walk_right + rotate: false + xy: 79, 116 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 3 +walk_right + rotate: false + xy: 42, 42 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 8 +walk_right + rotate: false + xy: 79, 79 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 5 diff --git a/source/core/assets/images/engineers/engineer.png b/source/core/assets/images/engineers/engineer.png new file mode 100644 index 0000000000000000000000000000000000000000..7f29b132e2f4a07f64777b5fe724ac0d5bdc8dfa GIT binary patch literal 6158 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 literal 0 HcmV?d00001 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 01e91b257..9b3e4ae83 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 @@ -1,6 +1,7 @@ package com.csse3200.game.components.player; import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.math.Vector2; import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; @@ -11,14 +12,16 @@ */ public class HumanAnimationController extends Component { // Event name constants - private static final String IDLE = "idleStart"; - private static final String RUN = "runStart"; + private static final String IDLE = "walkStop"; + private static final String WALKL = "walkLeftStart"; + private static final String WALKR = "walkRightStart"; private static final String FIRING = "firingStart"; private static final String HIT = "hitStart"; private static final String DEATH = "deathStart"; // Animation name constants private static final String IDLE_ANIM = "idle"; - private static final String RUN_ANIM = "run"; + private static final String WALKL_ANIM = "walk_left"; + private static final String WALKR_ANIM = "walk_right"; private static final String FIRE_ANIM = "firing"; private static final String HIT_ANIM = "hit"; private static final String DEATH_ANIM = "death"; @@ -43,7 +46,8 @@ public void create() { super.create(); animator = this.entity.getComponent(AnimationRenderComponent.class); entity.getEvents().addListener(IDLE, this::animateIdle); - entity.getEvents().addListener(RUN, this::animateRun); + entity.getEvents().addListener(WALKL, this::animateLeftWalk); + entity.getEvents().addListener(WALKR, this::animateRightWalk); entity.getEvents().addListener(FIRING, this::animateFiring); entity.getEvents().addListener(HIT, this::animateHit); entity.getEvents().addListener(DEATH, this::animateDeath); @@ -53,8 +57,12 @@ void animateIdle() { animator.startAnimation(IDLE_ANIM); } - void animateRun() { - animator.startAnimation(RUN_ANIM); + void animateLeftWalk() { + animator.startAnimation(WALKL_ANIM); +// runSound.play(); + } + void animateRightWalk() { + animator.startAnimation(WALKR_ANIM); // runSound.play(); } From 3f56d28cde3bd70e98159407aace39e224855671 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Tue, 5 Sep 2023 21:01:30 +1000 Subject: [PATCH 05/40] added more engineer visual assets, updated animationcontroller --- .../assets/images/engineers/engineer.atlas | 194 +++++++++++++++--- .../core/assets/images/engineers/engineer.png | Bin 6158 -> 15234 bytes .../player/HumanAnimationController.java | 16 +- 3 files changed, 178 insertions(+), 32 deletions(-) diff --git a/source/core/assets/images/engineers/engineer.atlas b/source/core/assets/images/engineers/engineer.atlas index 1cfc350ef..e033a1b8d 100644 --- a/source/core/assets/images/engineers/engineer.atlas +++ b/source/core/assets/images/engineers/engineer.atlas @@ -1,131 +1,271 @@ engineer.png -size: 128, 256 +size: 2048, 64 format: RGBA8888 filter: Nearest, Nearest repeat: none -idle +auto_shoot rotate: false - xy: 42, 42 + xy: 5, 6 + size: 52, 31 + orig: 52, 31 + offset: 0, 0 + index: 2 +auto_shoot + rotate: false + xy: 62, 6 + size: 52, 31 + orig: 52, 31 + offset: 0, 0 + index: 4 +auto_shoot + rotate: false + xy: 119, 6 + size: 52, 31 + orig: 52, 31 + offset: 0, 0 + index: 1 +auto_shoot + rotate: false + xy: 176, 6 + size: 52, 31 + orig: 52, 31 + offset: 0, 0 + index: 3 +death + rotate: false + xy: 270, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 - index: 8 + index: 6 +death + rotate: false + xy: 492, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 3 +death + rotate: false + xy: 714, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 5 +death + rotate: false + xy: 973, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 2 +death + rotate: false + xy: 1232, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 4 +death + rotate: false + xy: 1417, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 +hit + rotate: false + xy: 344, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 3 +hit + rotate: false + xy: 788, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 2 +hit + rotate: false + xy: 1269, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 +idle_left + rotate: false + xy: 381, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 2 +idle_left + rotate: false + xy: 640, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 4 +idle_left + rotate: false + xy: 825, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 +idle_left + rotate: false + xy: 1121, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 3 +idle_right + rotate: false + xy: 603, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 2 +idle_right + rotate: false + xy: 862, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 4 +idle_right + rotate: false + xy: 1047, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 +idle_right + rotate: false + xy: 1306, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 3 +default + rotate: false + xy: 1047, 5 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 walk_left rotate: false - xy: 5, 190 + xy: 233, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 6 walk_left rotate: false - xy: 5, 116 + xy: 455, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 3 walk_left rotate: false - xy: 42, 153 + xy: 529, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 8 walk_left rotate: false - xy: 5, 79 + xy: 677, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 5 walk_left rotate: false - xy: 5, 42 + xy: 936, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 2 walk_left rotate: false - xy: 42, 79 + xy: 1010, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 7 walk_left rotate: false - xy: 5, 5 + xy: 1158, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 4 walk_left rotate: false - xy: 42, 5 + xy: 1380, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 1 -default - rotate: false - xy: 5, 190 - size: 32, 32 - orig: 32, 32 - offset: 0, 0 - index: 6 walk_right rotate: false - xy: 5, 153 + xy: 307, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 2 walk_right rotate: false - xy: 42, 190 + xy: 418, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 7 walk_right rotate: false - xy: 79, 190 + xy: 566, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 4 walk_right rotate: false - xy: 42, 116 + xy: 751, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 1 walk_right rotate: false - xy: 79, 153 + xy: 899, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 6 walk_right rotate: false - xy: 79, 116 + xy: 1084, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 3 walk_right rotate: false - xy: 42, 42 + xy: 1195, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 index: 8 walk_right rotate: false - xy: 79, 79 + xy: 1343, 5 size: 32, 32 orig: 32, 32 offset: 0, 0 diff --git a/source/core/assets/images/engineers/engineer.png b/source/core/assets/images/engineers/engineer.png index 7f29b132e2f4a07f64777b5fe724ac0d5bdc8dfa..6deb3614299f9882e4cc92a9715622b94a7cd9fe 100644 GIT binary patch literal 15234 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 literal 6158 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 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 9b3e4ae83..77159036d 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 @@ -12,14 +12,16 @@ */ public class HumanAnimationController extends Component { // Event name constants - private static final String IDLE = "walkStop"; + private static final String IDLEL = "idleLeft"; + private static final String IDLER = "idleRight"; private static final String WALKL = "walkLeftStart"; private static final String WALKR = "walkRightStart"; private static final String FIRING = "firingStart"; private static final String HIT = "hitStart"; private static final String DEATH = "deathStart"; // Animation name constants - private static final String IDLE_ANIM = "idle"; + private static final String IDLEL_ANIM = "idle_left"; + private static final String IDLER_ANIM = "idle_right"; private static final String WALKL_ANIM = "walk_left"; private static final String WALKR_ANIM = "walk_right"; private static final String FIRE_ANIM = "firing"; @@ -45,7 +47,8 @@ public class HumanAnimationController extends Component { public void create() { super.create(); animator = this.entity.getComponent(AnimationRenderComponent.class); - entity.getEvents().addListener(IDLE, this::animateIdle); + entity.getEvents().addListener(IDLEL, this::animateIdleLeft); + entity.getEvents().addListener(IDLER, this::animateIdleRight); entity.getEvents().addListener(WALKL, this::animateLeftWalk); entity.getEvents().addListener(WALKR, this::animateRightWalk); entity.getEvents().addListener(FIRING, this::animateFiring); @@ -53,8 +56,11 @@ public void create() { entity.getEvents().addListener(DEATH, this::animateDeath); } - void animateIdle() { - animator.startAnimation(IDLE_ANIM); + void animateIdleLeft() { + animator.startAnimation(IDLEL_ANIM); + } + void animateIdleRight() { + animator.startAnimation(IDLER_ANIM); } void animateLeftWalk() { From a8ea714fe50fad31f2f36057f57be5733ea1b47a Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Tue, 5 Sep 2023 22:29:57 +1000 Subject: [PATCH 06/40] updated engineer visual assets --- .../assets/images/engineers/engineer.atlas | 154 +++++++++--------- .../core/assets/images/engineers/engineer.png | Bin 15234 -> 21189 bytes 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/source/core/assets/images/engineers/engineer.atlas b/source/core/assets/images/engineers/engineer.atlas index e033a1b8d..4db973b8c 100644 --- a/source/core/assets/images/engineers/engineer.atlas +++ b/source/core/assets/images/engineers/engineer.atlas @@ -1,272 +1,272 @@ engineer.png -size: 2048, 64 +size: 512, 256 format: RGBA8888 filter: Nearest, Nearest repeat: none -auto_shoot +firing rotate: false - xy: 5, 6 + xy: 25, 190 size: 52, 31 orig: 52, 31 offset: 0, 0 index: 2 -auto_shoot +firing rotate: false - xy: 62, 6 + xy: 25, 154 size: 52, 31 orig: 52, 31 offset: 0, 0 index: 4 -auto_shoot +firing rotate: false - xy: 119, 6 + xy: 102, 190 size: 52, 31 orig: 52, 31 offset: 0, 0 index: 1 -auto_shoot +firing rotate: false - xy: 176, 6 + xy: 25, 118 size: 52, 31 orig: 52, 31 offset: 0, 0 index: 3 death rotate: false - xy: 270, 5 - size: 32, 32 + xy: 25, 81 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 6 death rotate: false - xy: 492, 5 - size: 32, 32 + xy: 350, 189 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 3 death rotate: false - xy: 714, 5 - size: 32, 32 + xy: 330, 152 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 5 death rotate: false - xy: 973, 5 - size: 32, 32 + xy: 82, 79 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 2 death rotate: false - xy: 1232, 5 - size: 32, 32 + xy: 253, 78 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 4 death rotate: false - xy: 1417, 5 - size: 32, 32 + xy: 367, 41 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 1 hit rotate: false - xy: 344, 5 - size: 32, 32 + xy: 25, 7 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 3 hit rotate: false - xy: 788, 5 - size: 32, 32 + xy: 159, 115 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 2 hit rotate: false - xy: 1269, 5 - size: 32, 32 + xy: 253, 41 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 1 idle_left rotate: false - xy: 381, 5 - size: 32, 32 + xy: 179, 189 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 2 idle_left rotate: false - xy: 640, 5 - size: 32, 32 + xy: 216, 152 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 4 idle_left rotate: false - xy: 825, 5 - size: 32, 32 + xy: 216, 115 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 1 idle_left rotate: false - xy: 1121, 5 - size: 32, 32 + xy: 139, 41 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 3 idle_right rotate: false - xy: 603, 5 - size: 32, 32 + xy: 159, 152 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 2 idle_right rotate: false - xy: 862, 5 - size: 32, 32 + xy: 273, 115 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 4 idle_right rotate: false - xy: 1047, 5 - size: 32, 32 + xy: 82, 5 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 1 idle_right rotate: false - xy: 1306, 5 - size: 32, 32 + xy: 310, 78 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 3 default rotate: false - xy: 1047, 5 - size: 32, 32 + xy: 216, 115 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 1 walk_left rotate: false - xy: 233, 5 - size: 32, 32 + xy: 102, 153 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 6 walk_left rotate: false - xy: 455, 5 - size: 32, 32 + xy: 293, 189 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 3 walk_left rotate: false - xy: 529, 5 - size: 32, 32 + xy: 407, 189 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 8 walk_left rotate: false - xy: 677, 5 - size: 32, 32 + xy: 273, 152 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 5 walk_left rotate: false - xy: 936, 5 - size: 32, 32 + xy: 387, 115 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 2 walk_left rotate: false - xy: 1010, 5 - size: 32, 32 + xy: 82, 42 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 7 walk_left rotate: false - xy: 1158, 5 - size: 32, 32 + xy: 196, 78 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 4 walk_left rotate: false - xy: 1380, 5 - size: 32, 32 + xy: 367, 78 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 1 walk_right rotate: false - xy: 307, 5 - size: 32, 32 + xy: 25, 44 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 2 walk_right rotate: false - xy: 418, 5 - size: 32, 32 + xy: 236, 189 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 7 walk_right rotate: false - xy: 566, 5 - size: 32, 32 + xy: 102, 116 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 4 walk_right rotate: false - xy: 751, 5 - size: 32, 32 + xy: 387, 152 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 1 walk_right rotate: false - xy: 899, 5 - size: 32, 32 + xy: 330, 115 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 6 walk_right rotate: false - xy: 1084, 5 - size: 32, 32 + xy: 139, 78 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 3 walk_right rotate: false - xy: 1195, 5 - size: 32, 32 + xy: 196, 41 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 8 walk_right rotate: false - xy: 1343, 5 - size: 32, 32 + xy: 310, 41 + size: 52, 31 orig: 32, 32 offset: 0, 0 index: 5 diff --git a/source/core/assets/images/engineers/engineer.png b/source/core/assets/images/engineers/engineer.png index 6deb3614299f9882e4cc92a9715622b94a7cd9fe..acc36d01d04eb7a4c253f91e752f722553b0724d 100644 GIT binary patch literal 21189 zcmZsC1yqz#*X|$)(keZaih=?XlG1{Jw8YSzLw86c0@5YY-7%CP3>`8#lq6TC6oJn0N1e&OT?K_u0>LLO&}?6XF5!0001?%%_hk0KfzEuNZ(w*yxY0 zm_Z8wKqW%v;|Dbl{r%?acwH^m&BnWcWcp2QmFA^h<_U(~R|&GOKy~9mYMwRY3iuy$ z?P5|k%*Mu|vyZq7a2f=$Q!KH8-~!I}PVCe^Lmb{|qyBV94vGi$Po@#=&MWVxNZjU) zP==3EkIr6|1vi{6d}-0yEfY!x?Xbys9qML&CrkJ;sBGnTq3s26Rm|vj8RQ`7{J(yt zanBV7d6JDNgN-PYbHnfc%zx9-O?8wWVsAY2vUe={0R0UqcviSyxdlv4E~GgdV|wDmnc8QpnYs^d!N*%WQugHD zu|SC8doFz)!Ik|%!NY3owts(H@AvSz;STdF#wZ#Fo&xVpO|JQxHx>8H_1g$a(6MP%3`P5n5s=X z#cp;V`IngMjkFVTQ@OC0wI8!ZU(FJNj)l~!n3o5p%gxUd|cwIb-%hk5o?jU6Yt?E z0EQym_o)c{w2)>dZLJn;8Q*U&F!I#@R>6m{NVT(Zy67!#S#Lv>^p7(=H8pi7*{$!V z((>!qX`X7h>r?}BT_hVL#-gL?NGanly`bB?pIK$S>49SN2Jg+W@(Q*8YIyWCQjCkI z5_g$bNMFEhk>W1-%Ndn|$7yG|NTnfGO-w^{9BY!wD7_&M_hQ@_i@SLdA>I`~7Jmi6 z-QsPD>cJk%&{kp;k=1_bnSeK*eewnM@TG2{-;xzd1qNGbIto&U7!{mz8_R3qzB$rcq_94B3_ zpFRCP*J%0Xxohu(!*CR;o;n#wutfaq!Zt-`zIEwHhxlI}zLWs{_tVITqX_Cpj*M=c zl+kV;4cHmhR9Mp06aT9&GoL}EbzbU14SX{!fLFquxe_gH7ewdW`zA+q|Efecp59~E zqrUC3dJran)}&V1m-Dj>durf#&A)n~qfORDGZdy{ng7%6dgNJN!PNciX;*_r`qs

R+JmTcas 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 Cb`S0V literal 15234 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 From 382f4ae2d87c4c8d516a9805602b26b05a769419 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Tue, 5 Sep 2023 23:00:53 +1000 Subject: [PATCH 07/40] added firing sounds, update animationController --- .../assets/sounds/engineers/firing_auto.mp3 | Bin 0 -> 18696 bytes .../assets/sounds/engineers/firing_single.mp3 | Bin 0 -> 9517 bytes .../player/HumanAnimationController.java | 11 +++++++---- 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 source/core/assets/sounds/engineers/firing_auto.mp3 create mode 100644 source/core/assets/sounds/engineers/firing_single.mp3 diff --git a/source/core/assets/sounds/engineers/firing_auto.mp3 b/source/core/assets/sounds/engineers/firing_auto.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f0b68569da94dc418ef68e6d5228da2b3efdbc3c GIT binary patch literal 18696 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

Kew~4K&C^p> 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 B)%E}Y literal 0 HcmV?d00001 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 77159036d..10a92685c 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 @@ -29,15 +29,18 @@ public class HumanAnimationController extends Component { private static final String DEATH_ANIM = "death"; // Sound effects constants // private static final String RUN_SFX = "run/todeploy.mp3"; -// private static final String FIRE_SFX = "sounds/gun_shot_trimmed.mp3"; + private static final String FIRE_AUTO_SFX = "sounds/engineers/firing_auto.mp3"; +// private static final String FIRE_SINGLE_SFX = "sounds/engineers/firing_single.mp3"; // private static final String HIT_SFX = "sounds/stow.mp3"; // private static final String DEATH_SFX = "sounds/stow.mp3"; AnimationRenderComponent animator; // Sound runSound = ServiceLocator.getResourceService().getAsset( // RUN_SFX, Sound.class); -// Sound attackSound = ServiceLocator.getResourceService().getAsset( -// FIRE_SFX, Sound.class); + Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( + FIRE_AUTO_SFX, Sound.class); +// Sound fireSingleSound = ServiceLocator.getResourceService().getAsset( +// FIRE_SINGLE_SFX, Sound.class); // Sound hitSound = ServiceLocator.getResourceService().getAsset( // HIT_SFX, Sound.class); // Sound deathSound = ServiceLocator.getResourceService().getAsset( @@ -74,7 +77,7 @@ void animateRightWalk() { void animateFiring() { animator.startAnimation(FIRE_ANIM); -// attackSound.play(); + fireAutoSound.play(); } void animateHit() { From 5663ba6a2d07c6483e2a7b89b4f4f0c51dd79dff Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Tue, 5 Sep 2023 23:03:21 +1000 Subject: [PATCH 08/40] created Engineer Factory class --- .../entities/factories/EngineerFactory.java | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java new file mode 100644 index 000000000..bae88b5ee --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -0,0 +1,105 @@ +package com.csse3200.game.entities.factories; + +import com.badlogic.gdx.graphics.g2d.Animation; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.components.TouchAttackComponent; +import com.csse3200.game.components.npc.GhostAnimationController; +import com.csse3200.game.components.npc.XenoAnimationController; +import com.csse3200.game.components.player.HumanAnimationController; +import com.csse3200.game.components.tasks.ShootTask; +import com.csse3200.game.components.tasks.WanderTask; +import com.csse3200.game.components.tasks.human.HumanWanderTask; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.Melee; +import com.csse3200.game.entities.PredefinedWeapons; +import com.csse3200.game.entities.Weapon; +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; +import com.csse3200.game.rendering.TextureRenderComponent; +import com.csse3200.game.services.ServiceLocator; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Factory to create non-playable character (NPC) entities with predefined components. + * + *

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 EngineerFactory { + private static final EngineerConfigs configs = + FileLoader.readClass(EngineerConfigs.class, "configs/Engineers.json"); + + /** + * Creates an Engineer entity. + * + * @return entity + */ + public static Entity createEngineer() { + Entity engineer = createBaseHumanNPC(); + BaseEntityConfig config = configs.engineer; + + AnimationRenderComponent animator = new AnimationRenderComponent( + new TextureAtlas("images/engineers/engineer.atlas")); + animator.addAnimation("walk_left", 0.2f, Animation.PlayMode.LOOP); + animator.addAnimation("walk_right", 0.2f, Animation.PlayMode.LOOP); + animator.addAnimation("idle_right", 0.2f, Animation.PlayMode.LOOP); + animator.addAnimation("firing", 0.1f, Animation.PlayMode.NORMAL); + animator.addAnimation("hit", 0.1f, Animation.PlayMode.NORMAL); + animator.addAnimation("death", 0.1f, Animation.PlayMode.NORMAL); + + + engineer + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + .addComponent(animator) + .addComponent(new HumanAnimationController()); + + engineer.getComponent(AnimationRenderComponent.class).scaleEntity(); + engineer.setScale(1.5f, 1.2f); + return engineer; + } + + /** + * Creates a generic human npc to be used as a base entity by more specific NPC creation methods. + * + * @return entity + */ + public static Entity createBaseHumanNPC() { + AITaskComponent aiComponent = + new AITaskComponent() + .addTask(new HumanWanderTask(new Vector2(0, 10f), 2f)); + + Entity human = + new Entity() + .addComponent(new PhysicsComponent()) + .addComponent(new PhysicsMovementComponent()) + .addComponent(new ColliderComponent()) + .addComponent(new HitboxComponent().setLayer(PhysicsLayer.PLAYER)) + .addComponent(new TouchAttackComponent(PhysicsLayer.NPC, 1.5f)) + .addComponent(aiComponent); + + PhysicsUtils.setScaledCollider(human, 0.9f, 0.4f); + return human; + } + + private EngineerFactory() { + throw new IllegalStateException("Instantiating static util class"); + } +} + + From aa05c31e053dcb87aabfc4ba9a332fbf949063e3 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Tue, 5 Sep 2023 23:06:59 +1000 Subject: [PATCH 09/40] created human version of WanderTask with subtasks --- .../tasks/human/HumanMovementTask.java | 95 ++++++++++++++++++ .../components/tasks/human/HumanWaitTask.java | 39 ++++++++ .../tasks/human/HumanWanderTask.java | 96 +++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/human/HumanMovementTask.java create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/human/HumanWaitTask.java create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java 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 new file mode 100644 index 000000000..7b64f1fc7 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanMovementTask.java @@ -0,0 +1,95 @@ +package com.csse3200.game.components.tasks.human; + +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.physics.components.PhysicsMovementComponent; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Move to a given position, finishing when you get close enough. Requires an entity with a + * PhysicsMovementComponent. + */ +public class HumanMovementTask extends DefaultTask { + private static final Logger logger = LoggerFactory.getLogger(HumanMovementTask.class); + + private final GameTime gameTime; + private Vector2 target; + private float stopDistance = 0.01f; + private long lastTimeMoved; + private Vector2 lastPos; + private PhysicsMovementComponent movementComponent; + + public HumanMovementTask(Vector2 target) { + this.target = target; + this.gameTime = ServiceLocator.getTimeSource(); + } + + public HumanMovementTask(Vector2 target, float stopDistance) { + this(target); + this.stopDistance = stopDistance; + } + + @Override + public void start() { + super.start(); + this.movementComponent = owner.getEntity().getComponent(PhysicsMovementComponent.class); + movementComponent.setTarget(target); + movementComponent.setMoving(true); + + // Trigger the correct walk animation depending on the target location. + if (target.x < owner.getEntity().getPosition().x) { + owner.getEntity().getEvents().trigger("walkLeftStart"); + } else { + owner.getEntity().getEvents().trigger("walkRightStart"); + } + + logger.debug("Starting movement towards {}", target); + lastTimeMoved = gameTime.getTime(); + lastPos = owner.getEntity().getPosition(); + } + + @Override + public void update() { + if (isAtTarget()) { + movementComponent.setMoving(false); + status = Status.FINISHED; + logger.debug("Finished moving to {}", target); + } else { + checkIfStuck(); + } + } + + public void setTarget(Vector2 target) { + this.target = target; + movementComponent.setTarget(target); + } + + @Override + public void stop() { + super.stop(); + movementComponent.setMoving(false); + logger.debug("Stopping movement"); + } + + private boolean isAtTarget() { + return owner.getEntity().getPosition().dst(target) <= stopDistance; + } + + private void checkIfStuck() { + if (didMove()) { + lastTimeMoved = gameTime.getTime(); + lastPos = owner.getEntity().getPosition(); + } else if (gameTime.getTimeSince(lastTimeMoved) > 500L) { + movementComponent.setMoving(false); + status = Status.FAILED; + logger.debug("Got stuck! Failing movement task"); + } + } + + private boolean didMove() { + return owner.getEntity().getPosition().dst2(lastPos) > 0.001f; + } +} diff --git a/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWaitTask.java b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWaitTask.java new file mode 100644 index 000000000..06072c46b --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWaitTask.java @@ -0,0 +1,39 @@ +package com.csse3200.game.components.tasks.human; + +import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; + +/** + * Task that does nothing other than waiting for a given time. Status is Finished + * after the time has passed. + */ +public class HumanWaitTask extends DefaultTask { + private final GameTime timeSource; + private final float duration; + private long endTime; + + /** + * @param duration How long to wait for, in seconds. + */ + public HumanWaitTask(float duration) { + timeSource = ServiceLocator.getTimeSource(); + this.duration = duration; + } + + /** + * Start waiting from now until duration has passed. + */ + @Override + public void start() { + super.start(); + endTime = timeSource.getTime() + (int)(duration * 1000); + } + + @Override + public void update() { + if (timeSource.getTime() >= endTime) { + status = Status.FINISHED; + } + } +} 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 new file mode 100644 index 000000000..41fd44024 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java @@ -0,0 +1,96 @@ +package com.csse3200.game.components.tasks.human; + +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 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 HumanWanderTask extends DefaultTask implements PriorityTask { + private static final Logger logger = LoggerFactory.getLogger(HumanWanderTask.class); + + private final Vector2 wanderRange; + private final float waitTime; + private Vector2 startPos; + private HumanMovementTask movementTask; + private HumanWaitTask waitTask; + private Task currentTask; + + /** + * @param wanderRange Distance in X and Y the entity can move from its position when start() is + * called. + * @param waitTime How long in seconds to wait between wandering. + */ + public HumanWanderTask(Vector2 wanderRange, float waitTime) { + this.wanderRange = wanderRange; + this.waitTime = waitTime; + } + + @Override + public int getPriority() { + return 1; // Low priority task + } + + @Override + public void start() { + super.start(); + startPos = owner.getEntity().getPosition(); + + waitTask = new HumanWaitTask(waitTime); + waitTask.create(owner); + + movementTask = new HumanMovementTask(getDirection()); + movementTask.create(owner); + + movementTask.start(); + + currentTask = movementTask; + + + owner.getEntity().getEvents().trigger("idleRight"); + } + + @Override + public void update() { + if (currentTask.getStatus() != Status.ACTIVE) { + if (currentTask == movementTask) { + startWaiting(); + owner.getEntity().getEvents().trigger("idleRight"); + } else { + startMoving(); + } + } + currentTask.update(); + } + + private void startWaiting() { + logger.debug("Starting waiting"); + swapTask(waitTask); + } + + private void startMoving() { + logger.debug("Starting moving"); + movementTask.setTarget(getDirection()); + swapTask(movementTask); + } + + private void swapTask(Task newTask) { + if (currentTask != null) { + currentTask.stop(); + } + currentTask = newTask; + currentTask.start(); + } + + private Vector2 getDirection() { + float y = startPos.y; + return new Vector2(0, y); + } +} From 000847cedf540663ee39ddc90e5ea8c7a4ec3406 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Tue, 5 Sep 2023 23:08:29 +1000 Subject: [PATCH 10/40] created EngineerConfigs class --- .../game/entities/configs/EngineerConfigs.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java diff --git a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java new file mode 100644 index 000000000..e72d7d91e --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java @@ -0,0 +1,13 @@ +package com.csse3200.game.entities.configs; + +/** + * Defines the properties stored in Engineer config files to be loaded by the Engineer Factory. + */ +public class EngineerConfigs extends BaseEntityConfig { + public BaseEntityConfig engineer = new BaseEntityConfig(); + + public int health = 10; + public int baseAttack = 10; + + +} From 6cc7e0e3fcbd5fc0cb4374cd894b93a42b8dabaa Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Tue, 5 Sep 2023 23:09:37 +1000 Subject: [PATCH 11/40] added Engineers.json config file --- source/core/assets/configs/Engineers.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 source/core/assets/configs/Engineers.json diff --git a/source/core/assets/configs/Engineers.json b/source/core/assets/configs/Engineers.json new file mode 100644 index 000000000..3a733e8da --- /dev/null +++ b/source/core/assets/configs/Engineers.json @@ -0,0 +1,6 @@ +{ + "engineer" : { + "health": 50, + "baseAttack": 10 +} +} \ No newline at end of file From 081d9b21760a91f4aa809ffaa610619395494e4c Mon Sep 17 00:00:00 2001 From: praneetdhoolia Date: Wed, 6 Sep 2023 02:43:37 +1000 Subject: [PATCH 12/40] Created EngineerCombatTask, modified EngineerFactory to accommodate Engineer.json config. --- source/core/assets/configs/Engineer.json | 4 + .../components/tasks/EngineerCombatTask.java | 178 ++++++++++++++++++ .../entities/factories/EngineerFactory.java | 23 ++- 3 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 source/core/assets/configs/Engineer.json create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java diff --git a/source/core/assets/configs/Engineer.json b/source/core/assets/configs/Engineer.json new file mode 100644 index 000000000..8fdee5182 --- /dev/null +++ b/source/core/assets/configs/Engineer.json @@ -0,0 +1,4 @@ +{ + "health": 10, + "baseAttack": 5 +} \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java new file mode 100644 index 000000000..c43650af6 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java @@ -0,0 +1,178 @@ +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.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; + +/** + * 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 EngineerCombatTask extends DefaultTask implements PriorityTask { + + private static final int INTERVAL = 1; // The time interval for each target scan from the Engineer. + private static final short TARGET = PhysicsLayer.NPC; // The type of targets that the Engineer will detect. + + // Animation event names for the Engineer's state machine. + private static final String STOW = ""; + private static final String DEPLOY = ""; + private static final String FIRING = ""; + private static final String IDLE = ""; + private static final String DYING = ""; + + // The Engineer's attributes. + private final int priority; // The priority of this task within the task list. + private final float maxRange; // The maximum range of the Engineer's weapon. + + private Vector2 engineerPosition = new Vector2(10, 50); // Placeholder value for the Engineer's position. + private final Vector2 maxRangePosition = new Vector2(); + private PhysicsEngine physics; + private GameTime timeSource; + private long endTime; + private final RaycastHit hit = new RaycastHit(); + + /** The Engineer's states. */ + private enum STATE { + IDLE, DEPLOY, FIRING, STOW + } + private STATE engineerState = STATE.IDLE; + + /** + * @param priority The Engineer's combat task priority in the list of tasks. + * @param maxRange The maximum range of the Engineer's weapon. + */ + public EngineerCombatTask(int priority, float maxRange) { + this.priority = priority; + this.maxRange = maxRange; + physics = ServiceLocator.getPhysicsService().getPhysics(); + timeSource = ServiceLocator.getTimeSource(); + } + + /** + * Starts the Task running, triggers the initial "idleStart" event. + */ + @Override + public void start() { + super.start(); + // Set the tower's coordinates + this.engineerPosition = owner.getEntity().getCenterPosition(); + this.maxRangePosition.set(engineerPosition.x + maxRange, engineerPosition.y); + // Default to idle mode + owner.getEntity().getEvents().trigger(IDLE); + + endTime = timeSource.getTime() + (INTERVAL * 500); + } + + /** + * 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 (timeSource.getTime() >= endTime) { + updateEngineerState(); + endTime = timeSource.getTime() + (INTERVAL * 1000); + } + } + + /** + * Engineer state machine + */ + public void updateEngineerState() { + // configure tower state depending on target visibility + switch (engineerState) { + case IDLE -> { + // targets detected in idle mode - start deployment + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(DEPLOY); + engineerState = STATE.DEPLOY; + } + } + case DEPLOY -> { + // currently deploying, + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(FIRING); + engineerState = STATE.FIRING; + } else { + owner.getEntity().getEvents().trigger(STOW); + engineerState = STATE.STOW; + } + } + case FIRING -> { + // targets gone - stop firing + if (!isTargetVisible()) { + + owner.getEntity().getEvents().trigger(STOW); + engineerState = STATE.STOW; + } else { + owner.getEntity().getEvents().trigger(FIRING); + // this might be changed to an event which gets triggered everytime the tower enters the firing state + Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.75)); + ServiceLocator.getEntityService().register(newProjectile); + } + } + case STOW -> { + // currently stowing + if (isTargetVisible()) { + + owner.getEntity().getEvents().trigger(DEPLOY); + engineerState = STATE.DEPLOY; + } else { + owner.getEntity().getEvents().trigger(IDLE); + engineerState = STATE.IDLE; + } + } + } + } + /** + * For stopping the running task + */ + @Override + public void stop() { + super.stop(); + owner.getEntity().getEvents().trigger(STOW); + } + + /** + * Returns the current priority of the task. + * @return active priority value if targets detected, inactive priority otherwise + */ + @Override + public int getPriority() { + return isTargetVisible() ? getActivePriority() : getInactivePriority(); + } + + /** + * 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 + */ + private boolean isTargetVisible() { + // If there is an obstacle in the path to the max range point, mobs visible. + return physics.raycast(engineerPosition, maxRangePosition, TARGET, hit); + } +} diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index a11267e06..647b1fa59 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -1,5 +1,26 @@ package com.csse3200.game.entities.factories; -public class EngineerFactory { +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Animation; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; +import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.physics.PhysicsLayer; +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.files.FileLoader; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.rendering.TextureRenderComponent; +import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.entities.configs.EngineerConfig; +import com.csse3200.game.components.tasks.EngineerCombatTask; +public class EngineerFactory { + private static final EngineerConfig config = + FileLoader.readClass(EngineerConfig.class, "configs/Engineer.json"); } \ No newline at end of file From 81f1d2f7a730e868f022ddd959564af0557689cd Mon Sep 17 00:00:00 2001 From: praneetdhoolia Date: Wed, 6 Sep 2023 03:41:04 +1000 Subject: [PATCH 13/40] Pulled changes from Ahmad's branch. --- .../components/tasks/EngineerCombatTask.java | 2 +- .../entities/factories/EngineerFactory.java | 30 +------------------ 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java index 4a0e43171..c43650af6 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java @@ -6,7 +6,7 @@ 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;git pu +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; diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 9a6cb8546..1e43649ee 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -1,35 +1,10 @@ package com.csse3200.game.entities.factories; -<<<<<<< HEAD -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.Animation; -import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; -import com.csse3200.game.ai.tasks.AITaskComponent; -import com.csse3200.game.components.CombatStatsComponent; -import com.csse3200.game.entities.Entity; -import com.csse3200.game.physics.PhysicsLayer; -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.files.FileLoader; -import com.csse3200.game.rendering.AnimationRenderComponent; -import com.csse3200.game.rendering.TextureRenderComponent; -import com.csse3200.game.services.ServiceLocator; -import com.csse3200.game.entities.configs.EngineerConfig; -import com.csse3200.game.components.tasks.EngineerCombatTask; - -public class EngineerFactory { - private static final EngineerConfig config = - FileLoader.readClass(EngineerConfig.class, "configs/Engineer.json"); -} -======= import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.components.tasks.EngineerCombatTask; import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.TouchAttackComponent; import com.csse3200.game.components.npc.GhostAnimationController; @@ -127,6 +102,3 @@ private EngineerFactory() { throw new IllegalStateException("Instantiating static util class"); } } - - ->>>>>>> 6cc7e0e3fcbd5fc0cb4374cd894b93a42b8dabaa From 443c67a6ac82fec12237c23e71eb2214a7c833ae Mon Sep 17 00:00:00 2001 From: praneetdhoolia Date: Wed, 6 Sep 2023 04:08:48 +1000 Subject: [PATCH 14/40] Testing Engineer Entity --- .../com/csse3200/game/areas/ForestGameArea.java | 10 ++++++++++ .../player/HumanAnimationController.java | 6 +++--- .../game/entities/factories/EngineerFactory.java | 16 ++++++++++++---- 3 files changed, 25 insertions(+), 7 deletions(-) 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 c3e93e6ef..af7863e06 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -152,6 +152,7 @@ public void create() { spawnIncome(); spawnScrap(); + spawnEngineer(); bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); @@ -457,6 +458,15 @@ private void spawnIncome() { spawnEntityAt(towerfactory, randomPos, true, true); } } + + private void spawnEngineer() { + GridPoint2 minPos = new GridPoint2(0, 0); + GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); + GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); + + Entity engineer = EngineerFactory.createEngineer(); + spawnEntityAt(engineer, randomPos, true, true); + } } \ 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 10a92685c..28ff3cb48 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 @@ -37,8 +37,8 @@ public class HumanAnimationController extends Component { AnimationRenderComponent animator; // Sound runSound = ServiceLocator.getResourceService().getAsset( // RUN_SFX, Sound.class); - Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( - FIRE_AUTO_SFX, Sound.class); +// Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( +// FIRE_AUTO_SFX, Sound.class); // Sound fireSingleSound = ServiceLocator.getResourceService().getAsset( // FIRE_SINGLE_SFX, Sound.class); // Sound hitSound = ServiceLocator.getResourceService().getAsset( @@ -77,7 +77,7 @@ void animateRightWalk() { void animateFiring() { animator.startAnimation(FIRE_ANIM); - fireAutoSound.play(); +// fireAutoSound.play(); } void animateHit() { diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 1e43649ee..53e5eacf7 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -43,6 +43,9 @@ * similar characteristics. */ public class EngineerFactory { + + private static final int COMBAT_TASK_PRIORITY = 2; + private static final int ENGINEER_RANGE = 40; private static final EngineerConfigs configs = FileLoader.readClass(EngineerConfigs.class, "configs/Engineers.json"); @@ -54,6 +57,9 @@ public class EngineerFactory { public static Entity createEngineer() { Entity engineer = createBaseHumanNPC(); BaseEntityConfig config = configs.engineer; + AITaskComponent combatComponent = + new AITaskComponent() + .addTask(new EngineerCombatTask(COMBAT_TASK_PRIORITY, ENGINEER_RANGE)); AnimationRenderComponent animator = new AnimationRenderComponent( new TextureAtlas("images/engineers/engineer.atlas")); @@ -66,9 +72,10 @@ public static Entity createEngineer() { engineer - .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) - .addComponent(animator) - .addComponent(new HumanAnimationController()); + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + .addComponent(animator) + .addComponent(new HumanAnimationController()); + //.addComponent(combatComponent); engineer.getComponent(AnimationRenderComponent.class).scaleEntity(); engineer.setScale(1.5f, 1.2f); @@ -83,7 +90,8 @@ public static Entity createEngineer() { public static Entity createBaseHumanNPC() { AITaskComponent aiComponent = new AITaskComponent() - .addTask(new HumanWanderTask(new Vector2(0, 10f), 2f)); + .addTask(new HumanWanderTask(new Vector2(0, 10f), 2f)); + Entity human = new Entity() From c9a9e915961188ae69dc078f5051a157beb330bb Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 11:10:25 +1000 Subject: [PATCH 15/40] removed unused sound effects assets, updated comment --- .../game/components/player/HumanAnimationController.java | 7 +++---- .../csse3200/game/entities/factories/EngineerFactory.java | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) 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 10a92685c..0b5c8ea1e 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 @@ -1,7 +1,6 @@ package com.csse3200.game.components.player; import com.badlogic.gdx.audio.Sound; -import com.badlogic.gdx.math.Vector2; import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; @@ -28,11 +27,11 @@ public class HumanAnimationController extends Component { private static final String HIT_ANIM = "hit"; private static final String DEATH_ANIM = "death"; // Sound effects constants -// private static final String RUN_SFX = "run/todeploy.mp3"; +// private static final String RUN_SFX = ""; private static final String FIRE_AUTO_SFX = "sounds/engineers/firing_auto.mp3"; // private static final String FIRE_SINGLE_SFX = "sounds/engineers/firing_single.mp3"; -// private static final String HIT_SFX = "sounds/stow.mp3"; -// private static final String DEATH_SFX = "sounds/stow.mp3"; +// private static final String HIT_SFX = ""; +// private static final String DEATH_SFX = ""; AnimationRenderComponent animator; // Sound runSound = ServiceLocator.getResourceService().getAsset( diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index bae88b5ee..e15491627 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -46,7 +46,7 @@ public class EngineerFactory { FileLoader.readClass(EngineerConfigs.class, "configs/Engineers.json"); /** - * Creates an Engineer entity. + * Creates an Engineer entity, based on a base Human entity. * * @return entity */ From 9f11e537716c81f0c4070b299de239d76ee4c071 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 11:14:52 +1000 Subject: [PATCH 16/40] added scale constants, updated wanderTask range --- .../game/entities/factories/EngineerFactory.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index e15491627..cb6c11569 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -32,7 +32,9 @@ import java.util.Arrays; /** - * Factory to create non-playable character (NPC) entities with predefined components. + * 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 @@ -45,8 +47,12 @@ public class EngineerFactory { private static final EngineerConfigs configs = FileLoader.readClass(EngineerConfigs.class, "configs/Engineers.json"); + private static final float HUMAN_SCALE_X = 1.5f; + private static final float HUMAN_SCALE_Y = 1.2f; + /** - * Creates an Engineer entity, based on a base Human entity. + * Creates an Engineer entity, based on a base Human entity, with the appropriate components and animations + * * * @return entity */ @@ -70,7 +76,7 @@ public static Entity createEngineer() { .addComponent(new HumanAnimationController()); engineer.getComponent(AnimationRenderComponent.class).scaleEntity(); - engineer.setScale(1.5f, 1.2f); + engineer.setScale(HUMAN_SCALE_X, HUMAN_SCALE_Y); return engineer; } @@ -82,7 +88,7 @@ public static Entity createEngineer() { public static Entity createBaseHumanNPC() { AITaskComponent aiComponent = new AITaskComponent() - .addTask(new HumanWanderTask(new Vector2(0, 10f), 2f)); + .addTask(new HumanWanderTask(new Vector2(5, 10f), 2f)); Entity human = new Entity() From 962650a53283c1aab7c8dad4c570ed672d0437cb Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 12:59:38 +1000 Subject: [PATCH 17/40] created human specific CombatStatsComponent, and updated references --- .../csse3200/game/areas/ForestGameArea.java | 11 ++-- .../player/HumanCombatStatsComponent.java | 54 +++++++++++++++++++ .../tasks/human/HumanWanderTask.java | 12 +++++ .../entities/factories/EngineerFactory.java | 3 +- 4 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java 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 c3e93e6ef..a209f79bf 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -100,7 +100,8 @@ public class ForestGameArea extends GameArea { "sounds/Impact4.ogg", "sounds/towers/gun_shot_trimmed.mp3", "sounds/towers/deploy.mp3", - "sounds/towers/stow.mp3" + "sounds/towers/stow.mp3", + "sounds/engineers/firing_auto.mp3" }; private static final String backgroundMusic = "sounds/background/Sci-Fi1.ogg"; private static final String[] forestMusic = {backgroundMusic}; @@ -134,9 +135,9 @@ public void create() { displayUI(); spawnTerrain(); - spawnBuilding1(); - spawnBuilding2(); - spawnMountains(); +// spawnBuilding1(); +// spawnBuilding2(); +// spawnMountains(); player = spawnPlayer(); playMusic(); @@ -154,6 +155,8 @@ public void create() { bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); + Entity engineer = EngineerFactory.createEngineer(); + spawnEntityAt(engineer, new GridPoint2(5, 20), true, true); playMusic(); } diff --git a/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java b/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java new file mode 100644 index 000000000..32b1b135b --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java @@ -0,0 +1,54 @@ +package com.csse3200.game.components.player; + +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.components.Component; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.Melee; +import com.csse3200.game.entities.Weapon; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +/** + * Component used to store information related to combat such as health, attack, etc. Any entities + * which engage it combat should have an instance of this class registered. This class can be + * extended for more specific combat needs. + * + * health: the current health of the entity + * baseAttack: the base attack of the entity + * fullHealth: the full health of the entity + * state: the current state of the entity (full health above 66%, half health above 33%, low health below 33%) + * drops: the items that the entity drops when it dies + * closeRangeAbilities: the Melee abilities (close range) of the entity + * longRangeAbilities: the Projectile abilities (long range) of the entity + * + */ +public class HumanCombatStatsComponent extends CombatStatsComponent { + + private static final Logger logger = LoggerFactory.getLogger(HumanCombatStatsComponent.class); + private int health; + private int baseAttack; + private String state; + private ArrayList drops; + private ArrayList closeRangeAbilities; + private ArrayList longRangeAbilities; //TODO change String to Projectiles + + public HumanCombatStatsComponent(int health, int baseAttack) { + super(health, baseAttack); + } + + /** + * Decrease the health of the entity based on the damage provided. + * */ + public void hit(Integer damage) { + int newHealth = getHealth() - damage; + setHealth(newHealth); + entity.getEvents().trigger("hitStart"); + + changeState(); + } + + //TODO: this will be changed to drop an item and load it to the screen + +} 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 41fd44024..783f5857d 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 @@ -4,6 +4,8 @@ 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.components.player.HumanCombatStatsComponent; import com.csse3200.game.components.tasks.MovementTask; import com.csse3200.game.components.tasks.WaitTask; import org.slf4j.Logger; @@ -23,6 +25,8 @@ public class HumanWanderTask extends DefaultTask implements PriorityTask { private HumanWaitTask waitTask; private Task currentTask; + private boolean isDead = false; + /** * @param wanderRange Distance in X and Y the entity can move from its position when start() is * called. @@ -60,6 +64,14 @@ public void start() { @Override public void update() { if (currentTask.getStatus() != Status.ACTIVE) { + if (isDead) { +// owner.getEntity().dispose(); + // make the appropriate calls to decrement the human count. + } + if (!isDead && owner.getEntity().getComponent(HumanCombatStatsComponent.class).isDead()) { + owner.getEntity().getEvents().trigger("deathStart"); + // Add a time delay here to allow animation to play? + } if (currentTask == movementTask) { startWaiting(); owner.getEntity().getEvents().trigger("idleRight"); diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index cb6c11569..e61a50edb 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -9,6 +9,7 @@ import com.csse3200.game.components.npc.GhostAnimationController; import com.csse3200.game.components.npc.XenoAnimationController; import com.csse3200.game.components.player.HumanAnimationController; +import com.csse3200.game.components.player.HumanCombatStatsComponent; import com.csse3200.game.components.tasks.ShootTask; import com.csse3200.game.components.tasks.WanderTask; import com.csse3200.game.components.tasks.human.HumanWanderTask; @@ -71,7 +72,7 @@ public static Entity createEngineer() { engineer - .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + .addComponent(new HumanCombatStatsComponent(config.health, config.baseAttack)) .addComponent(animator) .addComponent(new HumanAnimationController()); From d5dd3d4893f847b5c03335ec16549f83de36b7e5 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 13:05:17 +1000 Subject: [PATCH 18/40] added Override decorator, removed ununsed imports and variables --- .../player/HumanCombatStatsComponent.java | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java b/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java index 32b1b135b..cc693d14e 100644 --- a/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java @@ -1,8 +1,6 @@ package com.csse3200.game.components.player; import com.csse3200.game.components.CombatStatsComponent; -import com.csse3200.game.components.Component; -import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.Melee; import com.csse3200.game.entities.Weapon; import org.slf4j.Logger; @@ -26,14 +24,6 @@ */ public class HumanCombatStatsComponent extends CombatStatsComponent { - private static final Logger logger = LoggerFactory.getLogger(HumanCombatStatsComponent.class); - private int health; - private int baseAttack; - private String state; - private ArrayList drops; - private ArrayList closeRangeAbilities; - private ArrayList longRangeAbilities; //TODO change String to Projectiles - public HumanCombatStatsComponent(int health, int baseAttack) { super(health, baseAttack); } @@ -41,14 +31,11 @@ public HumanCombatStatsComponent(int health, int baseAttack) { /** * Decrease the health of the entity based on the damage provided. * */ + @Override public void hit(Integer damage) { int newHealth = getHealth() - damage; setHealth(newHealth); entity.getEvents().trigger("hitStart"); - changeState(); } - - //TODO: this will be changed to drop an item and load it to the screen - } From 4ff5b0a711e1f94cdef770ff670946668ccc17a2 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 15:12:25 +1000 Subject: [PATCH 19/40] update changes --- .../com/csse3200/game/areas/ForestGameArea.java | 2 +- .../tasks/human/HumanMovementTask.java | 17 +++++++++-------- .../components/tasks/human/HumanWanderTask.java | 16 +++++++++------- .../entities/factories/EngineerFactory.java | 9 +++++---- 4 files changed, 24 insertions(+), 20 deletions(-) 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 a209f79bf..2db29b800 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -155,7 +155,7 @@ public void create() { bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); - Entity engineer = EngineerFactory.createEngineer(); + Entity engineer = EngineerFactory.createEngineer(player); spawnEntityAt(engineer, new GridPoint2(5, 20), true, true); playMusic(); 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 7b64f1fc7..00ed0d673 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 @@ -2,6 +2,7 @@ import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.entities.Entity; import com.csse3200.game.physics.components.PhysicsMovementComponent; import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; @@ -16,18 +17,18 @@ public class HumanMovementTask extends DefaultTask { private static final Logger logger = LoggerFactory.getLogger(HumanMovementTask.class); private final GameTime gameTime; - private Vector2 target; + private Entity target; private float stopDistance = 0.01f; private long lastTimeMoved; private Vector2 lastPos; private PhysicsMovementComponent movementComponent; - public HumanMovementTask(Vector2 target) { + public HumanMovementTask(Entity target) { this.target = target; this.gameTime = ServiceLocator.getTimeSource(); } - public HumanMovementTask(Vector2 target, float stopDistance) { + public HumanMovementTask(Entity target, float stopDistance) { this(target); this.stopDistance = stopDistance; } @@ -36,11 +37,11 @@ public HumanMovementTask(Vector2 target, float stopDistance) { public void start() { super.start(); this.movementComponent = owner.getEntity().getComponent(PhysicsMovementComponent.class); - movementComponent.setTarget(target); + movementComponent.setTarget(target.getPosition()); movementComponent.setMoving(true); // Trigger the correct walk animation depending on the target location. - if (target.x < owner.getEntity().getPosition().x) { + if (target.getPosition().x < owner.getEntity().getPosition().x) { owner.getEntity().getEvents().trigger("walkLeftStart"); } else { owner.getEntity().getEvents().trigger("walkRightStart"); @@ -62,9 +63,9 @@ public void update() { } } - public void setTarget(Vector2 target) { + public void setTarget(Entity target) { this.target = target; - movementComponent.setTarget(target); + movementComponent.setTarget(target.getPosition()); } @Override @@ -75,7 +76,7 @@ public void stop() { } private boolean isAtTarget() { - return owner.getEntity().getPosition().dst(target) <= stopDistance; + return owner.getEntity().getPosition().dst(target.getPosition()) <= stopDistance; } private void checkIfStuck() { 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 783f5857d..7562da7fc 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 @@ -8,6 +8,7 @@ import com.csse3200.game.components.player.HumanCombatStatsComponent; import com.csse3200.game.components.tasks.MovementTask; import com.csse3200.game.components.tasks.WaitTask; +import com.csse3200.game.entities.Entity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,7 +19,7 @@ public class HumanWanderTask extends DefaultTask implements PriorityTask { private static final Logger logger = LoggerFactory.getLogger(HumanWanderTask.class); - private final Vector2 wanderRange; + private final Entity wanderRange; private final float waitTime; private Vector2 startPos; private HumanMovementTask movementTask; @@ -32,8 +33,8 @@ public class HumanWanderTask extends DefaultTask implements PriorityTask { * called. * @param waitTime How long in seconds to wait between wandering. */ - public HumanWanderTask(Vector2 wanderRange, float waitTime) { - this.wanderRange = wanderRange; + public HumanWanderTask(Entity target, float waitTime) { + this.wanderRange = target; this.waitTime = waitTime; } @@ -50,7 +51,7 @@ public void start() { waitTask = new HumanWaitTask(waitTime); waitTask.create(owner); - movementTask = new HumanMovementTask(getDirection()); + movementTask = new HumanMovementTask(this.wanderRange); movementTask.create(owner); movementTask.start(); @@ -89,7 +90,7 @@ private void startWaiting() { private void startMoving() { logger.debug("Starting moving"); - movementTask.setTarget(getDirection()); + movementTask.setTarget(this.wanderRange); swapTask(movementTask); } @@ -102,7 +103,8 @@ private void swapTask(Task newTask) { } private Vector2 getDirection() { - float y = startPos.y; - return new Vector2(0, y); +// float y = startPos.y; +// return new Vector2(0, y); + return this.wanderRange.getPosition(); } } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index e61a50edb..1b2e8f138 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.areas.GameArea; import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.TouchAttackComponent; import com.csse3200.game.components.npc.GhostAnimationController; @@ -57,8 +58,8 @@ public class EngineerFactory { * * @return entity */ - public static Entity createEngineer() { - Entity engineer = createBaseHumanNPC(); + public static Entity createEngineer(Entity target) { + Entity engineer = createBaseHumanNPC(target); BaseEntityConfig config = configs.engineer; AnimationRenderComponent animator = new AnimationRenderComponent( @@ -86,10 +87,10 @@ public static Entity createEngineer() { * * @return entity */ - public static Entity createBaseHumanNPC() { + public static Entity createBaseHumanNPC(Entity target) { AITaskComponent aiComponent = new AITaskComponent() - .addTask(new HumanWanderTask(new Vector2(5, 10f), 2f)); + .addTask(new HumanWanderTask(target, 2f)); Entity human = new Entity() From e3b92aab04504ed672c3f1fc58f1dd73e99ddb2b Mon Sep 17 00:00:00 2001 From: praneetdhoolia Date: Wed, 6 Sep 2023 15:47:40 +1000 Subject: [PATCH 20/40] Added Shooting Functionality to Engineers. --- .../csse3200/game/areas/ForestGameArea.java | 24 ++------ .../components/tasks/EngineerCombatTask.java | 58 +++++++++++-------- .../entities/factories/EngineerFactory.java | 6 +- 3 files changed, 39 insertions(+), 49 deletions(-) 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 af7863e06..28f16d103 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -129,34 +129,18 @@ public ForestGameArea(TerrainFactory terrainFactory) { /** Create the game area, including terrain, static entities (trees), dynamic entities (player) */ @Override public void create() { + // Load game assets loadAssets(); - displayUI(); - spawnTerrain(); - spawnBuilding1(); - spawnBuilding2(); - spawnMountains(); - player = spawnPlayer(); - playMusic(); - - // Types of projectile - spawnAoeProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f), 1); - spawnProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f)); - spawnMultiProjectile(new Vector2(0, 10), player, towardsMobs, 20, new Vector2(2f, 2f), 7); - spawnXenoGrunts(); - - spawnGhosts(); + + // Spawn Entities + player = spawnPlayer(); spawnWeaponTower(); - spawnIncome(); - spawnScrap(); - spawnEngineer(); bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); - - playMusic(); } private void displayUI() { diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java index c43650af6..4e836fc6b 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java @@ -24,9 +24,10 @@ public class EngineerCombatTask extends DefaultTask implements PriorityTask { // Animation event names for the Engineer's state machine. private static final String STOW = ""; private static final String DEPLOY = ""; - private static final String FIRING = ""; - private static final String IDLE = ""; - private static final String DYING = ""; + private static final String FIRING = "firingStart"; + private static final String IDLE_LEFT = "idleLeft"; + private static final String IDLE_RIGHT = "idleRight"; + private static final String DYING = "deathStart"; // The Engineer's attributes. private final int priority; // The priority of this task within the task list. @@ -41,9 +42,9 @@ public class EngineerCombatTask extends DefaultTask implements PriorityTask { /** The Engineer's states. */ private enum STATE { - IDLE, DEPLOY, FIRING, STOW + IDLE_LEFT, IDLE_RIGHT, DEPLOY, FIRING, STOW } - private STATE engineerState = STATE.IDLE; + private STATE engineerState = STATE.IDLE_RIGHT; /** * @param priority The Engineer's combat task priority in the list of tasks. @@ -57,7 +58,7 @@ public EngineerCombatTask(int priority, float maxRange) { } /** - * Starts the Task running, triggers the initial "idleStart" event. + * Runs the task and triggers Engineer's idle animation. */ @Override public void start() { @@ -66,7 +67,7 @@ public void start() { this.engineerPosition = owner.getEntity().getCenterPosition(); this.maxRangePosition.set(engineerPosition.x + maxRange, engineerPosition.y); // Default to idle mode - owner.getEntity().getEvents().trigger(IDLE); + owner.getEntity().getEvents().trigger(IDLE_RIGHT); endTime = timeSource.getTime() + (INTERVAL * 500); } @@ -89,11 +90,18 @@ public void update() { public void updateEngineerState() { // configure tower state depending on target visibility switch (engineerState) { - case IDLE -> { + case IDLE_LEFT -> { // targets detected in idle mode - start deployment if (isTargetVisible()) { - owner.getEntity().getEvents().trigger(DEPLOY); - engineerState = STATE.DEPLOY; + owner.getEntity().getEvents().trigger(FIRING); + engineerState = STATE.FIRING; + } + } + case IDLE_RIGHT -> { + // targets detected in idle mode - start deployment + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(FIRING); + engineerState = STATE.FIRING; } } case DEPLOY -> { @@ -110,8 +118,8 @@ public void updateEngineerState() { // targets gone - stop firing if (!isTargetVisible()) { - owner.getEntity().getEvents().trigger(STOW); - engineerState = STATE.STOW; + owner.getEntity().getEvents().trigger(IDLE_RIGHT); + engineerState = STATE.IDLE_RIGHT; } else { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state @@ -120,17 +128,17 @@ public void updateEngineerState() { ServiceLocator.getEntityService().register(newProjectile); } } - case STOW -> { - // currently stowing - if (isTargetVisible()) { - - owner.getEntity().getEvents().trigger(DEPLOY); - engineerState = STATE.DEPLOY; - } else { - owner.getEntity().getEvents().trigger(IDLE); - engineerState = STATE.IDLE; - } - } +// case STOW -> { +// // currently stowing +// if (isTargetVisible()) { +// +// owner.getEntity().getEvents().trigger(DEPLOY); +// engineerState = STATE.DEPLOY; +// } else { +// owner.getEntity().getEvents().trigger(IDLE); +// engineerState = STATE.IDLE; +// } +// } } } /** @@ -139,7 +147,7 @@ public void updateEngineerState() { @Override public void stop() { super.stop(); - owner.getEntity().getEvents().trigger(STOW); + owner.getEntity().getEvents().trigger(IDLE_RIGHT); } /** @@ -173,6 +181,6 @@ private int getInactivePriority() { */ private boolean isTargetVisible() { // If there is an obstacle in the path to the max range point, mobs visible. - return physics.raycast(engineerPosition, maxRangePosition, TARGET, hit); + return physics.raycast(owner.getEntity().getCenterPosition(), maxRangePosition, TARGET, hit); } } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 53e5eacf7..b7dfdc6f0 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -57,9 +57,6 @@ public class EngineerFactory { public static Entity createEngineer() { Entity engineer = createBaseHumanNPC(); BaseEntityConfig config = configs.engineer; - AITaskComponent combatComponent = - new AITaskComponent() - .addTask(new EngineerCombatTask(COMBAT_TASK_PRIORITY, ENGINEER_RANGE)); AnimationRenderComponent animator = new AnimationRenderComponent( new TextureAtlas("images/engineers/engineer.atlas")); @@ -75,9 +72,10 @@ public static Entity createEngineer() { .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) .addComponent(animator) .addComponent(new HumanAnimationController()); - //.addComponent(combatComponent); engineer.getComponent(AnimationRenderComponent.class).scaleEntity(); + engineer.getComponent(AITaskComponent.class) + .addTask(new EngineerCombatTask(COMBAT_TASK_PRIORITY, ENGINEER_RANGE)); engineer.setScale(1.5f, 1.2f); return engineer; } From 7a97d53fc6cf67b0a132c460c6bf60be05734bd2 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 20:10:23 +1000 Subject: [PATCH 21/40] removed unused imports --- .../entities/factories/EngineerFactory.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 1b2e8f138..db99a1391 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -2,22 +2,12 @@ import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.AITaskComponent; -import com.csse3200.game.areas.GameArea; import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.TouchAttackComponent; -import com.csse3200.game.components.npc.GhostAnimationController; -import com.csse3200.game.components.npc.XenoAnimationController; import com.csse3200.game.components.player.HumanAnimationController; -import com.csse3200.game.components.player.HumanCombatStatsComponent; -import com.csse3200.game.components.tasks.ShootTask; -import com.csse3200.game.components.tasks.WanderTask; import com.csse3200.game.components.tasks.human.HumanWanderTask; import com.csse3200.game.entities.Entity; -import com.csse3200.game.entities.Melee; -import com.csse3200.game.entities.PredefinedWeapons; -import com.csse3200.game.entities.Weapon; import com.csse3200.game.entities.configs.*; import com.csse3200.game.files.FileLoader; import com.csse3200.game.physics.PhysicsLayer; @@ -27,11 +17,6 @@ import com.csse3200.game.physics.components.PhysicsComponent; import com.csse3200.game.physics.components.PhysicsMovementComponent; import com.csse3200.game.rendering.AnimationRenderComponent; -import com.csse3200.game.rendering.TextureRenderComponent; -import com.csse3200.game.services.ServiceLocator; - -import java.util.ArrayList; -import java.util.Arrays; /** * Factory to create non-playable human character (NPC) entities with predefined components. @@ -73,7 +58,7 @@ public static Entity createEngineer(Entity target) { engineer - .addComponent(new HumanCombatStatsComponent(config.health, config.baseAttack)) + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) .addComponent(animator) .addComponent(new HumanAnimationController()); From 9d10e9e143f1fe5883df1d7f7959431f3b9c2bd9 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 21:11:54 +1000 Subject: [PATCH 22/40] re-enabled checkAndDeleteBodies, Engineers now die --- source/core/assets/configs/Engineers.json | 4 +-- .../csse3200/game/areas/ForestGameArea.java | 1 + .../tasks/human/HumanMovementTask.java | 1 + .../tasks/human/HumanWanderTask.java | 34 +++++++++++-------- .../entities/configs/EngineerConfigs.java | 2 +- .../entities/factories/EngineerFactory.java | 6 ++-- .../game/entities/factories/NPCFactory.java | 6 ++-- .../csse3200/game/physics/PhysicsEngine.java | 2 +- 8 files changed, 32 insertions(+), 24 deletions(-) diff --git a/source/core/assets/configs/Engineers.json b/source/core/assets/configs/Engineers.json index 3a733e8da..caa7c9a1c 100644 --- a/source/core/assets/configs/Engineers.json +++ b/source/core/assets/configs/Engineers.json @@ -1,6 +1,6 @@ { "engineer" : { - "health": 50, - "baseAttack": 10 + "health": 20, + "baseAttack": 5 } } \ No newline at end of file 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 2db29b800..02d13745e 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -5,6 +5,7 @@ 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.input.DropInputComponent; import com.csse3200.game.areas.terrain.TerrainFactory; import com.csse3200.game.areas.terrain.TerrainFactory.TerrainType; 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 00ed0d673..b25c9f7a2 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 @@ -56,6 +56,7 @@ public void start() { public void update() { if (isAtTarget()) { movementComponent.setMoving(false); + owner.getEntity().getEvents().trigger("idleStart"); status = Status.FINISHED; logger.debug("Finished moving to {}", target); } else { 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 7562da7fc..33480cfc0 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 @@ -5,10 +5,9 @@ import com.csse3200.game.ai.tasks.PriorityTask; import com.csse3200.game.ai.tasks.Task; import com.csse3200.game.components.CombatStatsComponent; -import com.csse3200.game.components.player.HumanCombatStatsComponent; -import com.csse3200.game.components.tasks.MovementTask; -import com.csse3200.game.components.tasks.WaitTask; import com.csse3200.game.entities.Entity; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.services.GameTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,11 +24,12 @@ public class HumanWanderTask extends DefaultTask implements PriorityTask { private HumanMovementTask movementTask; private HumanWaitTask waitTask; private Task currentTask; + private GameTime endTime; private boolean isDead = false; /** - * @param wanderRange Distance in X and Y the entity can move from its position when start() is + * @param target Distance in X and Y the entity can move from its position when start() is * called. * @param waitTime How long in seconds to wait between wandering. */ @@ -51,7 +51,7 @@ public void start() { waitTask = new HumanWaitTask(waitTime); waitTask.create(owner); - movementTask = new HumanMovementTask(this.wanderRange); + movementTask = new HumanMovementTask(this.wanderRange, 1f); movementTask.create(owner); movementTask.start(); @@ -59,20 +59,22 @@ public void start() { currentTask = movementTask; - owner.getEntity().getEvents().trigger("idleRight"); +// owner.getEntity().getEvents().trigger("idleRight"); } @Override public void update() { + if (isDead && owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { + owner.getEntity().setFlagForDelete(true); + // make the appropriate calls to decrement the human count. + } + if (!isDead && owner.getEntity().getComponent(CombatStatsComponent.class).isDead()) { + owner.getEntity().getEvents().trigger("deathStart"); + // Add a time delay here to allow animation to play? + isDead = true; + } if (currentTask.getStatus() != Status.ACTIVE) { - if (isDead) { -// owner.getEntity().dispose(); - // make the appropriate calls to decrement the human count. - } - if (!isDead && owner.getEntity().getComponent(HumanCombatStatsComponent.class).isDead()) { - owner.getEntity().getEvents().trigger("deathStart"); - // Add a time delay here to allow animation to play? - } + if (currentTask == movementTask) { startWaiting(); owner.getEntity().getEvents().trigger("idleRight"); @@ -83,6 +85,10 @@ public void update() { currentTask.update(); } + private void isHit() { + + } + private void startWaiting() { logger.debug("Starting waiting"); swapTask(waitTask); diff --git a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java index e72d7d91e..e5b5159d7 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java @@ -7,7 +7,7 @@ public class EngineerConfigs extends BaseEntityConfig { public BaseEntityConfig engineer = new BaseEntityConfig(); public int health = 10; - public int baseAttack = 10; + public int baseAttack = 1; } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index db99a1391..3d591777b 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -34,8 +34,8 @@ public class EngineerFactory { private static final EngineerConfigs configs = FileLoader.readClass(EngineerConfigs.class, "configs/Engineers.json"); - private static final float HUMAN_SCALE_X = 1.5f; - private static final float HUMAN_SCALE_Y = 1.2f; + 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 @@ -75,7 +75,7 @@ public static Entity createEngineer(Entity target) { public static Entity createBaseHumanNPC(Entity target) { AITaskComponent aiComponent = new AITaskComponent() - .addTask(new HumanWanderTask(target, 2f)); + .addTask(new HumanWanderTask(target, 1f)); Entity human = new Entity() diff --git a/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java index dd7777f79..18d2e509d 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java @@ -113,9 +113,9 @@ public static Entity createXenoGrunt(Entity target) { new AnimationRenderComponent( ServiceLocator.getResourceService().getAsset("images/mobs/xenoGruntRunning.atlas", TextureAtlas.class)); animator.addAnimation("xeno_run", 0.1f, Animation.PlayMode.LOOP); - animator.addAnimation("xeno_shoot", 0.1f, Animation.PlayMode.NORMAL); - animator.addAnimation("xeno_melee", 0.1f, Animation.PlayMode.NORMAL); - animator.addAnimation("xeno_die", 0.1f, Animation.PlayMode.NORMAL); +// animator.addAnimation("xeno_shoot", 0.1f, Animation.PlayMode.NORMAL); +// animator.addAnimation("xeno_melee", 0.1f, Animation.PlayMode.NORMAL); +// animator.addAnimation("xeno_die", 0.1f, Animation.PlayMode.NORMAL); xenoGrunt .addComponent(new CombatStatsComponent(config.fullHeath, config.baseAttack, drops, melee, projectiles)) diff --git a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java index f4db690cb..b1f1421fa 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java @@ -45,7 +45,7 @@ public PhysicsEngine(World world, GameTime timeSource) { public void update() { // Check for deleted bodies and joints -// checkAndDeleteBodies(); + checkAndDeleteBodies(); // Updating physics isn't as easy as triggering an update every frame. Each frame could take a // different amount of time to run, but physics simulations are only stable if computed at a From 9958c1c776fc4097e68ba8333889423600a44cf9 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 22:36:48 +1000 Subject: [PATCH 23/40] added hit event triggering in hit methods --- .../com/csse3200/game/components/CombatStatsComponent.java | 6 ++++++ .../com/csse3200/game/entities/factories/NPCFactory.java | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/CombatStatsComponent.java b/source/core/src/main/com/csse3200/game/components/CombatStatsComponent.java index a860b6547..2f157f887 100644 --- a/source/core/src/main/com/csse3200/game/components/CombatStatsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/CombatStatsComponent.java @@ -127,12 +127,18 @@ public void setBaseAttack(int attack) { public void hit(Integer damage) { int newHealth = getHealth() - damage; setHealth(newHealth); + if (entity != null && !this.isDead()) { + entity.getEvents().trigger("hitStart"); + } changeState(); } // Default CombatStatsComponent that relies on the attacker's combatStatsComponent. public void hit(CombatStatsComponent attacker) { int newHealth = getHealth() - attacker.getBaseAttack(); + if (entity != null && !this.isDead()) { + entity.getEvents().trigger("hitStart"); + } setHealth(newHealth); } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java index 18d2e509d..4005f8818 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java @@ -135,8 +135,8 @@ public static Entity createXenoGrunt(Entity target) { public static Entity createBaseNPC(Entity target) { AITaskComponent aiComponent = new AITaskComponent() - .addTask(new WanderTask(new Vector2(2f, 2f), 2f)) - .addTask(new ShootTask(target, 10, 3f, 4f)); + .addTask(new WanderTask(new Vector2(2f, 2f), 2f)); +// .addTask(new ShootTask(target, 10, 3f, 4f)); //.addTask(new ChaseTask(target, 10, 3f, 4f)); Entity npc = new Entity() From 8317df921827b39e398de53da0c77d3463c633b3 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 22:37:21 +1000 Subject: [PATCH 24/40] added null checking in checkAndDeleteBodies --- .../csse3200/game/physics/PhysicsEngine.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java index b1f1421fa..1b7878d44 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java @@ -91,16 +91,18 @@ public void checkAndDeleteBodies() { world.getBodies(bodies); // Check for bodies to be deleted - for(Body body : bodies) { - Entity entity = ((BodyUserData) body.getUserData()).entity; - - // If the entity is flagged for deletion, destroy the body before world.step() is called - if(entity.getFlagForDelete()) { - logger.debug("Destroying physics body {}", body); - ProjectileDestructors.destroyProjectile(entity); - - // Make sure not to delete the body twice - entity.setFlagForDelete(false); + for (Body body : bodies) { + // check for null values + if (body.getUserData() != null) { + Entity entity = ((BodyUserData) body.getUserData()).entity; + // If the entity is flagged for deletion, destroy the body before world.step() is called + if (entity.getFlagForDelete()) { + logger.debug("Destroying physics body {}", body); + ProjectileDestructors.destroyProjectile(entity); + + // Make sure not to delete the body twice + entity.setFlagForDelete(false); + } } } } From 27e18c62c8081d3a49f971b45e6fc008ae8d19a0 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 22:37:43 +1000 Subject: [PATCH 25/40] modified engineers to take damage and die --- source/core/assets/configs/Engineers.json | 2 +- .../tasks/human/HumanWanderTask.java | 39 ++++++++++--------- .../entities/factories/EngineerFactory.java | 2 +- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/source/core/assets/configs/Engineers.json b/source/core/assets/configs/Engineers.json index caa7c9a1c..35848e3c2 100644 --- a/source/core/assets/configs/Engineers.json +++ b/source/core/assets/configs/Engineers.json @@ -1,6 +1,6 @@ { "engineer" : { - "health": 20, + "health": 100, "baseAttack": 5 } } \ No newline at end of file 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 33480cfc0..cc27a6b6a 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 @@ -6,6 +6,9 @@ import com.csse3200.game.ai.tasks.Task; import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.entities.Entity; +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.GameTime; import org.slf4j.Logger; @@ -57,36 +60,34 @@ public void start() { movementTask.start(); currentTask = movementTask; - - -// owner.getEntity().getEvents().trigger("idleRight"); } @Override public void update() { - if (isDead && owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { - owner.getEntity().setFlagForDelete(true); - // make the appropriate calls to decrement the human count. - } 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; } - if (currentTask.getStatus() != Status.ACTIVE) { - - if (currentTask == movementTask) { - startWaiting(); - owner.getEntity().getEvents().trigger("idleRight"); - } else { - startMoving(); + else if (isDead && owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { + owner.getEntity().setFlagForDelete(true); + // make the appropriate calls to decrement the human count. + } + else if (!isDead) { + if (currentTask.getStatus() != Status.ACTIVE) { + + if (currentTask == movementTask) { + startWaiting(); + owner.getEntity().getEvents().trigger("idleRight"); + } else { + startMoving(); + } } + currentTask.update(); } - currentTask.update(); - } - - private void isHit() { - } private void startWaiting() { diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 3d591777b..7e5a69091 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -53,7 +53,7 @@ public static Entity createEngineer(Entity target) { animator.addAnimation("walk_right", 0.2f, Animation.PlayMode.LOOP); animator.addAnimation("idle_right", 0.2f, Animation.PlayMode.LOOP); animator.addAnimation("firing", 0.1f, Animation.PlayMode.NORMAL); - animator.addAnimation("hit", 0.1f, Animation.PlayMode.NORMAL); + animator.addAnimation("hit", 0.01f, Animation.PlayMode.NORMAL); animator.addAnimation("death", 0.1f, Animation.PlayMode.NORMAL); From 354b55f3eee619ce8b4f64c11aa01381bf7ac4dc Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Wed, 6 Sep 2023 22:40:13 +1000 Subject: [PATCH 26/40] commented checkAndDeleteBodies out for investigation of alternative approach --- .../core/src/main/com/csse3200/game/physics/PhysicsEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java index 1b7878d44..882ad4935 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java @@ -45,7 +45,7 @@ public PhysicsEngine(World world, GameTime timeSource) { public void update() { // Check for deleted bodies and joints - checkAndDeleteBodies(); + // checkAndDeleteBodies(); // Updating physics isn't as easy as triggering an update every frame. Each frame could take a // different amount of time to run, but physics simulations are only stable if computed at a From 8ab5f3a5e88f11ff01781a68a0d7898a81c23bef Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Thu, 7 Sep 2023 15:32:00 +1000 Subject: [PATCH 27/40] updated PhysicalLayer class with new layers, and updated references in Factories --- .../player/HumanCombatStatsComponent.java | 41 ------------------- .../entities/factories/BossKingFactory.java | 2 +- .../entities/factories/EngineerFactory.java | 2 +- .../game/entities/factories/NPCFactory.java | 2 +- .../entities/factories/PlayerFactory.java | 2 +- .../entities/factories/ProjectileFactory.java | 2 +- .../game/entities/factories/TowerFactory.java | 9 ++-- .../csse3200/game/physics/PhysicsLayer.java | 5 ++- .../factories/EngineerFactoryTest.java | 4 ++ 9 files changed, 18 insertions(+), 51 deletions(-) delete mode 100644 source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java create mode 100644 source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java diff --git a/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java b/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java deleted file mode 100644 index cc693d14e..000000000 --- a/source/core/src/main/com/csse3200/game/components/player/HumanCombatStatsComponent.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.csse3200.game.components.player; - -import com.csse3200.game.components.CombatStatsComponent; -import com.csse3200.game.entities.Melee; -import com.csse3200.game.entities.Weapon; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; - -/** - * Component used to store information related to combat such as health, attack, etc. Any entities - * which engage it combat should have an instance of this class registered. This class can be - * extended for more specific combat needs. - * - * health: the current health of the entity - * baseAttack: the base attack of the entity - * fullHealth: the full health of the entity - * state: the current state of the entity (full health above 66%, half health above 33%, low health below 33%) - * drops: the items that the entity drops when it dies - * closeRangeAbilities: the Melee abilities (close range) of the entity - * longRangeAbilities: the Projectile abilities (long range) of the entity - * - */ -public class HumanCombatStatsComponent extends CombatStatsComponent { - - public HumanCombatStatsComponent(int health, int baseAttack) { - super(health, baseAttack); - } - - /** - * Decrease the health of the entity based on the damage provided. - * */ - @Override - public void hit(Integer damage) { - int newHealth = getHealth() - damage; - setHealth(newHealth); - entity.getEvents().trigger("hitStart"); - changeState(); - } -} diff --git a/source/core/src/main/com/csse3200/game/entities/factories/BossKingFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/BossKingFactory.java index 1bd29a628..b4260b2be 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/BossKingFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/BossKingFactory.java @@ -101,7 +101,7 @@ public static Entity createBaseBoss(Entity target) { .addComponent(new ColliderComponent()) .addComponent(new PhysicsMovementComponent()) .addComponent(new HitboxComponent().setLayer(PhysicsLayer.NPC)) - .addComponent(new TouchAttackComponent(PhysicsLayer.PLAYER, 1.5f)); + .addComponent(new TouchAttackComponent(PhysicsLayer.HUMANS, 1.5f)); PhysicsUtils.setScaledCollider(boss, 0.9f, 0.4f); diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 7e5a69091..df7789b13 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -82,7 +82,7 @@ public static Entity createBaseHumanNPC(Entity target) { .addComponent(new PhysicsComponent()) .addComponent(new PhysicsMovementComponent()) .addComponent(new ColliderComponent()) - .addComponent(new HitboxComponent().setLayer(PhysicsLayer.PLAYER)) + .addComponent(new HitboxComponent().setLayer(PhysicsLayer.ENGINEER)) .addComponent(new TouchAttackComponent(PhysicsLayer.NPC, 1.5f)) .addComponent(aiComponent); diff --git a/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java index 4005f8818..23afdbaac 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/NPCFactory.java @@ -144,7 +144,7 @@ public static Entity createBaseNPC(Entity target) { .addComponent(new PhysicsMovementComponent()) .addComponent(new ColliderComponent()) .addComponent(new HitboxComponent().setLayer(PhysicsLayer.NPC)) - .addComponent(new TouchAttackComponent(PhysicsLayer.PLAYER, 1.5f)) + .addComponent(new TouchAttackComponent(PhysicsLayer.HUMANS, 1.5f)) .addComponent(aiComponent); PhysicsUtils.setScaledCollider(npc, 0.9f, 0.4f); diff --git a/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java index 6a407c950..da5f1a40e 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java @@ -39,7 +39,7 @@ public static Entity createPlayer() { .addComponent(new TextureRenderComponent("images/box_boy_leaf.png")) .addComponent(new PhysicsComponent()) .addComponent(new ColliderComponent()) - .addComponent(new HitboxComponent().setLayer(PhysicsLayer.PLAYER)) + .addComponent(new HitboxComponent().setLayer(PhysicsLayer.ENGINEER)) .addComponent(new PlayerActions()) .addComponent(new CombatStatsComponent(stats.health, stats.baseAttack)) .addComponent(new InventoryComponent(stats.gold)) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index 33d596599..92da28717 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -44,7 +44,7 @@ public static Entity createFireBall(Entity target, Vector2 destination, Vector2 .addComponent(new ColliderComponent().setSensor(true)) // This is the component that allows the projectile to damage a specified target. - .addComponent(new TouchAttackComponent(PhysicsLayer.PLAYER, 1.5f, true)) + .addComponent(new TouchAttackComponent(PhysicsLayer.ENGINEER, 1.5f, true)) .addComponent(new CombatStatsComponent(config.health, config.baseAttack)); projectile diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index 04dab2a54..ffe48d4b4 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -37,6 +37,7 @@ public class TowerFactory { private static final int COMBAT_TASK_PRIORITY = 2; private static final int WEAPON_TOWER_MAX_RANGE = 40; private static final String WALL_IMAGE = "images/towers/wallTower.png"; + private static final String RESOURCE_TOWER = "images/towers/mine_tower.png"; private static final String TURRET_ATLAS = "images/towers/turret01.atlas"; private static final String IDLE_ANIM = "idle"; private static final float IDLE_SPEED = 0.3f; @@ -46,7 +47,7 @@ public class TowerFactory { private static final float STOW_SPEED = 0.2f; private static final String FIRE_ANIM = "firing"; private static final float FIRE_SPEED = 0.25f; - private static final int INCOME_INTERVAL = 300; + private static final int INCOME_INTERVAL = 3; private static final int INCOME_TASK_PRIORITY = 1; private static final baseTowerConfigs configs = @@ -60,7 +61,7 @@ public static Entity createIncomeTower() { IncomeTowerConfig config = configs.income; // Create the CurrencyIncomeTask and add it to the AITaskComponent - CurrencyTask currencyTask = new CurrencyTask(INCOME_TASK_PRIORITY, 3); + CurrencyTask currencyTask = new CurrencyTask(INCOME_TASK_PRIORITY, INCOME_INTERVAL); int updatedInterval = 1; currencyTask.setInterval(updatedInterval); @@ -69,7 +70,7 @@ public static Entity createIncomeTower() { income .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) .addComponent(new CostComponent(config.cost)) - .addComponent(new TextureRenderComponent("images/towers/mine_tower.png")) + .addComponent(new TextureRenderComponent(RESOURCE_TOWER)) .addComponent(aiTaskComponent); @@ -129,7 +130,7 @@ public static Entity createBaseTower() { // we're going to add more components later on Entity tower = new Entity() .addComponent(new ColliderComponent()) - .addComponent(new HitboxComponent().setLayer(PhysicsLayer.OBSTACLE)) // TODO: we might have to change the names of the layers + .addComponent(new HitboxComponent().setLayer(PhysicsLayer.TOWER)) // TODO: we might have to change the names of the layers .addComponent(new PhysicsComponent().setBodyType(BodyType.StaticBody)); return tower; 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 637776166..73c5904aa 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java @@ -3,12 +3,15 @@ public class PhysicsLayer { public static final short NONE = 0; public static final short DEFAULT = (1 << 0); - public static final short PLAYER = (1 << 1); + public static final short ENGINEER = (1 << 1); // Terrain obstacle, e.g. trees public static final short OBSTACLE = (1 << 2); // NPC (Non-Playable Character) colliders public static final short NPC = (1 << 3); public static final short PROJECTILE = (1 << 4); + public static final short TOWER = (1 << 5); + + public static final short HUMANS = (1 << 1) | (1 << 5); public static final short ALL = ~0; public static boolean contains(short filterBits, short layer) { 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 new file mode 100644 index 000000000..c6bd4fee2 --- /dev/null +++ b/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java @@ -0,0 +1,4 @@ +import static org.junit.jupiter.api.Assertions.*; +class EngineerFactoryTest { + +} \ No newline at end of file From 5a17e3975a6bc7f448aef2bc8ff5ddae0d6bfa90 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Thu, 7 Sep 2023 15:32:52 +1000 Subject: [PATCH 28/40] updated humanWanderTask and started writing tests --- .../tasks/human/HumanWanderTask.java | 2 - .../entities/configs/EngineerConfigs.java | 3 - .../player/HumanAnimationControllerTest.java | 50 ++++++++++++++ .../tasks/human/HumanMovementTaskTest.java | 34 ++++++++++ .../tasks/human/HumanWanderTaskTest.java | 30 +++++++++ .../factories/EngineerFactoryTest.java | 66 ++++++++++++++++++- 6 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java create mode 100644 source/core/src/test/com/csse3200/game/components/tasks/human/HumanMovementTaskTest.java create mode 100644 source/core/src/test/com/csse3200/game/components/tasks/human/HumanWanderTaskTest.java 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 cc27a6b6a..e3272ee06 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 @@ -10,7 +10,6 @@ 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.GameTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,7 +26,6 @@ public class HumanWanderTask extends DefaultTask implements PriorityTask { private HumanMovementTask movementTask; private HumanWaitTask waitTask; private Task currentTask; - private GameTime endTime; private boolean isDead = false; diff --git a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java index e5b5159d7..21113b7a0 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java @@ -5,9 +5,6 @@ */ public class EngineerConfigs extends BaseEntityConfig { public BaseEntityConfig engineer = new BaseEntityConfig(); - public int health = 10; public int baseAttack = 1; - - } 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 new file mode 100644 index 000000000..bf104bfbb --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java @@ -0,0 +1,50 @@ +package com.csse3200.game.components.player; + +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 HumanAnimationControllerTest { + + @BeforeEach + void setUp() { + } + + @AfterEach + void tearDown() { + } + + @Test + void create() { + } + + @Test + void animateIdleLeft() { + } + + @Test + void animateIdleRight() { + } + + @Test + void animateLeftWalk() { + } + + @Test + void animateRightWalk() { + } + + @Test + void animateFiring() { + } + + @Test + void animateHit() { + } + + @Test + void animateDeath() { + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..4deb83386 --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/human/HumanMovementTaskTest.java @@ -0,0 +1,34 @@ +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 new file mode 100644 index 000000000..5a24deb7c --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/human/HumanWanderTaskTest.java @@ -0,0 +1,30 @@ +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 HumanWanderTaskTest { + + @BeforeEach + void setUp() { + } + + @AfterEach + void tearDown() { + } + + @Test + void getPriority() { + } + + @Test + void start() { + } + + @Test + void update() { + } +} \ 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 c6bd4fee2..424e463c3 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 @@ -1,4 +1,68 @@ +package com.csse3200.game.entities.factories; + +import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.rendering.DebugRenderer; +import com.csse3200.game.rendering.RenderService; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; + + import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(GameExtension.class) class EngineerFactoryTest { - + Entity target = new Entity(); + private String[] texture = { + "images/engineers/engineer.png" + }; + private String[] atlas = {"images/engineers/engineer.atlas"}; + private static final String[] sounds = { + "sounds/engineers/firing_auto.mp3" + }; + + @BeforeEach + void setUp() { + GameTime gameTime = mock(GameTime.class); + when(gameTime.getDeltaTime()).thenReturn(0.02f); + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + 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(); + } + + @AfterEach + void tearDown() { + + } + + @Test + void createEngineer() { + Entity engineer = EngineerFactory.createEngineer(target); + assertNotNull(engineer); + } + + @Test + void createBaseHumanNPC() { + Entity human = EngineerFactory.createBaseHumanNPC(target); + assertNotNull(human); + } } \ No newline at end of file From f95d5d7103d816280a8b20113d736d80d354fc31 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Thu, 7 Sep 2023 21:55:10 +1000 Subject: [PATCH 29/40] modified EngineerCombatTask target scanning and integrated into HumanWanderTask --- .../components/tasks/EngineerCombatTask.java | 105 ++++++++++-------- .../tasks/human/HumanMovementTask.java | 16 +-- .../tasks/human/HumanWanderTask.java | 44 +++++--- 3 files changed, 97 insertions(+), 68 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java index 4e836fc6b..ac248ac59 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java @@ -11,6 +11,8 @@ import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; +import java.util.ArrayList; + /** * 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 @@ -30,15 +32,21 @@ public class EngineerCombatTask extends DefaultTask implements PriorityTask { private static final String DYING = "deathStart"; // The Engineer's attributes. - private final int priority; // The priority of this task within the task list. private final float maxRange; // The maximum range of the Engineer's weapon. + // weaponCapacity is the number of shots fired before the engineer has to reload + private static final int weaponCapacity = 10; + private int shotsFired = 0; // Tracks the number of shots fired in the current cycle private Vector2 engineerPosition = new Vector2(10, 50); // Placeholder value for the Engineer's position. private final Vector2 maxRangePosition = new Vector2(); private PhysicsEngine physics; 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<>(); /** The Engineer's states. */ private enum STATE { @@ -47,11 +55,9 @@ private enum STATE { private STATE engineerState = STATE.IDLE_RIGHT; /** - * @param priority The Engineer's combat task priority in the list of tasks. * @param maxRange The maximum range of the Engineer's weapon. */ - public EngineerCombatTask(int priority, float maxRange) { - this.priority = priority; + public EngineerCombatTask(float maxRange) { this.maxRange = maxRange; physics = ServiceLocator.getPhysicsService().getPhysics(); timeSource = ServiceLocator.getTimeSource(); @@ -95,6 +101,8 @@ public void updateEngineerState() { if (isTargetVisible()) { owner.getEntity().getEvents().trigger(FIRING); engineerState = STATE.FIRING; + } else { + } } case IDLE_RIGHT -> { @@ -117,28 +125,31 @@ public void updateEngineerState() { case FIRING -> { // targets gone - stop firing if (!isTargetVisible()) { - + owner.getEntity().getEvents().trigger(IDLE_RIGHT); engineerState = STATE.IDLE_RIGHT; } else { - owner.getEntity().getEvents().trigger(FIRING); - // this might be changed to an event which gets triggered everytime the tower enters the firing state - Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.75)); - ServiceLocator.getEntityService().register(newProjectile); + if (shotsFired <= 10) { + owner.getEntity().getEvents().trigger(FIRING); + // this might be changed to an event which gets triggered everytime the tower enters the firing state + Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), + new Vector2(100, owner.getEntity().getPosition().y), +// fetchTarget(), + new Vector2(4f, 4f)); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); + ServiceLocator.getEntityService().register(newProjectile); + shotsFired += 1; + reloadTime = timeSource.getTime(); + } else { + // engineer needs to reload + if (reloadTime < timeSource.getTime()) { + // engineer has reloaded + shotsFired = 0; + reloadTime = timeSource.getTime(); + } + } } } -// case STOW -> { -// // currently stowing -// if (isTargetVisible()) { -// -// owner.getEntity().getEvents().trigger(DEPLOY); -// engineerState = STATE.DEPLOY; -// } else { -// owner.getEntity().getEvents().trigger(IDLE); -// engineerState = STATE.IDLE; -// } -// } } } /** @@ -147,40 +158,40 @@ public void updateEngineerState() { @Override public void stop() { super.stop(); - owner.getEntity().getEvents().trigger(IDLE_RIGHT); } - - /** - * Returns the current priority of the task. - * @return active priority value if targets detected, inactive priority otherwise - */ + @Override public int getPriority() { - return isTargetVisible() ? getActivePriority() : getInactivePriority(); - } - - /** - * 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; + return isTargetVisible() ? 3 : 0; } - - /** - * 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 */ - private boolean isTargetVisible() { + public boolean isTargetVisible() { // If there is an obstacle in the path to the max range point, mobs visible. - return physics.raycast(owner.getEntity().getCenterPosition(), maxRangePosition, TARGET, hit); + Vector2 position = owner.getEntity().getCenterPosition(); + + for (int i = 5; i > -5; i--) { + if (physics.raycast(position, new Vector2(position.x + maxRange, position.y + i), TARGET, hit)) { + hits.add(hit); + targets.add(new Vector2(position.x + maxRange, position.y + i)); + } + } + return !hits.isEmpty(); + } + + public Vector2 fetchTarget() { + int lowest = 10; + Vector2 nearest = new Vector2(owner.getEntity().getCenterPosition().x, + owner.getEntity().getCenterPosition().y); + for (Vector2 tgt : targets){ + if (Math.abs(tgt.y - nearest.y) < lowest) { + lowest = (int)Math.abs(tgt.y - nearest.y); + nearest = tgt; + } + } + return nearest; } } 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 b25c9f7a2..6e957e311 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 @@ -17,18 +17,18 @@ public class HumanMovementTask extends DefaultTask { private static final Logger logger = LoggerFactory.getLogger(HumanMovementTask.class); private final GameTime gameTime; - private Entity target; + private Vector2 target; private float stopDistance = 0.01f; private long lastTimeMoved; private Vector2 lastPos; private PhysicsMovementComponent movementComponent; - public HumanMovementTask(Entity target) { + public HumanMovementTask(Vector2 target) { this.target = target; this.gameTime = ServiceLocator.getTimeSource(); } - public HumanMovementTask(Entity target, float stopDistance) { + public HumanMovementTask(Vector2 target, float stopDistance) { this(target); this.stopDistance = stopDistance; } @@ -37,11 +37,11 @@ public HumanMovementTask(Entity target, float stopDistance) { public void start() { super.start(); this.movementComponent = owner.getEntity().getComponent(PhysicsMovementComponent.class); - movementComponent.setTarget(target.getPosition()); + movementComponent.setTarget(target); movementComponent.setMoving(true); // Trigger the correct walk animation depending on the target location. - if (target.getPosition().x < owner.getEntity().getPosition().x) { + if (target.x < owner.getEntity().getPosition().x) { owner.getEntity().getEvents().trigger("walkLeftStart"); } else { owner.getEntity().getEvents().trigger("walkRightStart"); @@ -64,9 +64,9 @@ public void update() { } } - public void setTarget(Entity target) { + public void setTarget(Vector2 target) { this.target = target; - movementComponent.setTarget(target.getPosition()); + movementComponent.setTarget(target); } @Override @@ -77,7 +77,7 @@ public void stop() { } private boolean isAtTarget() { - return owner.getEntity().getPosition().dst(target.getPosition()) <= stopDistance; + return owner.getEntity().getPosition().dst(target) <= stopDistance; } private void checkIfStuck() { 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 e3272ee06..be2c570f7 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 @@ -5,6 +5,7 @@ import com.csse3200.game.ai.tasks.PriorityTask; import com.csse3200.game.ai.tasks.Task; import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.components.tasks.EngineerCombatTask; import com.csse3200.game.entities.Entity; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.components.ColliderComponent; @@ -20,23 +21,24 @@ public class HumanWanderTask extends DefaultTask implements PriorityTask { private static final Logger logger = LoggerFactory.getLogger(HumanWanderTask.class); - private final Entity wanderRange; + private float maxRange; + private Vector2 wanderRange; private final float waitTime; private Vector2 startPos; private HumanMovementTask movementTask; private HumanWaitTask waitTask; + + private EngineerCombatTask combatTask; private Task currentTask; private boolean isDead = false; /** - * @param target Distance in X and Y the entity can move from its position when start() is - * called. * @param waitTime How long in seconds to wait between wandering. */ - public HumanWanderTask(Entity target, float waitTime) { - this.wanderRange = target; + public HumanWanderTask(float waitTime, float maxRange) { this.waitTime = waitTime; + this.maxRange = maxRange; } @Override @@ -48,20 +50,24 @@ public int getPriority() { public void start() { super.start(); startPos = owner.getEntity().getPosition(); - + this.wanderRange = owner.getEntity().getCenterPosition(); waitTask = new HumanWaitTask(waitTime); waitTask.create(owner); movementTask = new HumanMovementTask(this.wanderRange, 1f); movementTask.create(owner); - movementTask.start(); + combatTask = new EngineerCombatTask(maxRange); + combatTask.create(owner); + combatTask.start(); + currentTask = movementTask; } @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); @@ -70,18 +76,25 @@ public void update() { // Add a time delay here to allow animation to play? isDead = true; } + // Check if engineer has finished dying else if (isDead && owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { owner.getEntity().setFlagForDelete(true); - // make the appropriate calls to decrement the human count. + // TODO: make the appropriate calls to decrement the human count. } + // otherwise doing engineer things else if (!isDead) { if (currentTask.getStatus() != Status.ACTIVE) { if (currentTask == movementTask) { startWaiting(); owner.getEntity().getEvents().trigger("idleRight"); - } else { - startMoving(); + } 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)); + } } } currentTask.update(); @@ -93,12 +106,17 @@ private void startWaiting() { swapTask(waitTask); } - private void startMoving() { + private void startMoving(Vector2 destination) { logger.debug("Starting moving"); - movementTask.setTarget(this.wanderRange); + movementTask.setTarget(destination); swapTask(movementTask); } + private void startCombat() { + logger.debug("Starting Combat"); + swapTask(combatTask); + } + private void swapTask(Task newTask) { if (currentTask != null) { currentTask.stop(); @@ -110,6 +128,6 @@ private void swapTask(Task newTask) { private Vector2 getDirection() { // float y = startPos.y; // return new Vector2(0, y); - return this.wanderRange.getPosition(); + return this.wanderRange; } } From 581177503d4bd32c28f7039546c5d68418e12874 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Thu, 7 Sep 2023 21:55:50 +1000 Subject: [PATCH 30/40] modified EngineerFactory to add integrated HumanWanderTask --- .../entities/factories/EngineerFactory.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 41839c749..f263a9542 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -34,7 +34,7 @@ public class EngineerFactory { private static final int COMBAT_TASK_PRIORITY = 2; - private static final int ENGINEER_RANGE = 40; + private static final int ENGINEER_RANGE = 10; private static final EngineerConfigs configs = FileLoader.readClass(EngineerConfigs.class, "configs/Engineers.json"); @@ -56,20 +56,21 @@ public static Entity createEngineer(Entity target) { animator.addAnimation("walk_left", 0.2f, Animation.PlayMode.LOOP); animator.addAnimation("walk_right", 0.2f, Animation.PlayMode.LOOP); animator.addAnimation("idle_right", 0.2f, Animation.PlayMode.LOOP); - animator.addAnimation("firing", 0.1f, Animation.PlayMode.NORMAL); + animator.addAnimation("firing", 0.01f, Animation.PlayMode.NORMAL); animator.addAnimation("hit", 0.01f, Animation.PlayMode.NORMAL); animator.addAnimation("death", 0.1f, Animation.PlayMode.NORMAL); + AITaskComponent aiComponent = new AITaskComponent(); engineer .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) .addComponent(animator) - .addComponent(new HumanAnimationController()); + .addComponent(new HumanAnimationController()) + .addComponent(aiComponent); + engineer.getComponent(AITaskComponent.class).addTask(new HumanWanderTask(COMBAT_TASK_PRIORITY, ENGINEER_RANGE)); engineer.getComponent(AnimationRenderComponent.class).scaleEntity(); engineer.setScale(HUMAN_SCALE_X, HUMAN_SCALE_Y); - engineer.getComponent(AITaskComponent.class) - .addTask(new EngineerCombatTask(COMBAT_TASK_PRIORITY, ENGINEER_RANGE)); return engineer; } @@ -79,9 +80,9 @@ public static Entity createEngineer(Entity target) { * @return entity */ public static Entity createBaseHumanNPC(Entity target) { - AITaskComponent aiComponent = - new AITaskComponent() - .addTask(new HumanWanderTask(target, 1f)); +// AITaskComponent aiComponent = +// new AITaskComponent() +// .addTask(new HumanWanderTask(target, 1f)); Entity human = new Entity() @@ -89,8 +90,8 @@ public static Entity createBaseHumanNPC(Entity target) { .addComponent(new PhysicsMovementComponent()) .addComponent(new ColliderComponent()) .addComponent(new HitboxComponent().setLayer(PhysicsLayer.ENGINEER)) - .addComponent(new TouchAttackComponent(PhysicsLayer.NPC, 1.5f)) - .addComponent(aiComponent); + .addComponent(new TouchAttackComponent(PhysicsLayer.NPC, 1.5f)); + PhysicsUtils.setScaledCollider(human, 0.9f, 0.4f); return human; From fb36e2564cd277ec17ea1f663ff7bea9360dc635 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Thu, 7 Sep 2023 21:56:33 +1000 Subject: [PATCH 31/40] modified ProjectileFactory for testing purposes. --- .../com/csse3200/game/entities/factories/ProjectileFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index 92da28717..cb02aa1fa 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -44,7 +44,7 @@ public static Entity createFireBall(Entity target, Vector2 destination, Vector2 .addComponent(new ColliderComponent().setSensor(true)) // This is the component that allows the projectile to damage a specified target. - .addComponent(new TouchAttackComponent(PhysicsLayer.ENGINEER, 1.5f, true)) + .addComponent(new TouchAttackComponent(PhysicsLayer.NPC, 1.5f, true)) .addComponent(new CombatStatsComponent(config.health, config.baseAttack)); projectile From 120dd796b26045069053fad2561ac51468b001e1 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Thu, 7 Sep 2023 22:10:14 +1000 Subject: [PATCH 32/40] removed unnecessary target arg in EngineerFactory --- .../com/csse3200/game/areas/ForestGameArea.java | 8 ++++---- .../game/entities/factories/EngineerFactory.java | 13 +++++-------- .../entities/factories/EngineerFactoryTest.java | 8 ++++++-- 3 files changed, 15 insertions(+), 14 deletions(-) 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 0e4e8aeee..57184f20e 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -140,7 +140,7 @@ public void create() { // Spawn Entities player = spawnPlayer(); spawnWeaponTower(); - spawnEngineer(player); + spawnEngineer(); bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); } @@ -445,12 +445,12 @@ private void spawnIncome() { } } - private void spawnEngineer(Entity target) { + private void spawnEngineer() { GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); + GridPoint2 maxPos = new GridPoint2(5, terrain.getMapBounds(0).sub(2, 2).y); GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity engineer = EngineerFactory.createEngineer(target); + Entity engineer = EngineerFactory.createEngineer(); spawnEntityAt(engineer, randomPos, true, true); } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index f263a9542..8f5972e85 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -3,7 +3,6 @@ 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.tasks.EngineerCombatTask; import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.TouchAttackComponent; import com.csse3200.game.components.player.HumanAnimationController; @@ -47,8 +46,8 @@ public class EngineerFactory { * * @return entity */ - public static Entity createEngineer(Entity target) { - Entity engineer = createBaseHumanNPC(target); + public static Entity createEngineer() { + Entity engineer = createBaseHumanNPC(); BaseEntityConfig config = configs.engineer; AnimationRenderComponent animator = new AnimationRenderComponent( @@ -56,7 +55,7 @@ public static Entity createEngineer(Entity target) { animator.addAnimation("walk_left", 0.2f, Animation.PlayMode.LOOP); animator.addAnimation("walk_right", 0.2f, Animation.PlayMode.LOOP); animator.addAnimation("idle_right", 0.2f, Animation.PlayMode.LOOP); - animator.addAnimation("firing", 0.01f, Animation.PlayMode.NORMAL); + animator.addAnimation("firing", 0.05f, Animation.PlayMode.NORMAL); animator.addAnimation("hit", 0.01f, Animation.PlayMode.NORMAL); animator.addAnimation("death", 0.1f, Animation.PlayMode.NORMAL); @@ -79,10 +78,8 @@ public static Entity createEngineer(Entity target) { * * @return entity */ - public static Entity createBaseHumanNPC(Entity target) { -// AITaskComponent aiComponent = -// new AITaskComponent() -// .addTask(new HumanWanderTask(target, 1f)); + public static Entity createBaseHumanNPC() { + Entity human = new Entity() 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 424e463c3..02b9f6f0b 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 @@ -2,6 +2,7 @@ import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.entities.Entity; import com.csse3200.game.extensions.GameExtension; import com.csse3200.game.physics.PhysicsService; @@ -56,13 +57,16 @@ void tearDown() { @Test void createEngineer() { - Entity engineer = EngineerFactory.createEngineer(target); + Entity engineer = EngineerFactory.createEngineer(); + engineer.setPosition(10f, 10f); assertNotNull(engineer); + assert(!engineer.getFlagForDelete()); + assert engineer.getPosition().x == 10f && engineer.getPosition().y == 10f; } @Test void createBaseHumanNPC() { - Entity human = EngineerFactory.createBaseHumanNPC(target); + Entity human = EngineerFactory.createBaseHumanNPC(); assertNotNull(human); } } \ No newline at end of file From bfc544d9e162a4ce041148bbf3141828f9aa7c78 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Fri, 8 Sep 2023 11:00:56 +1000 Subject: [PATCH 33/40] added tests to EngineerFactoryTest --- .../factories/EngineerFactoryTest.java | 140 ++++++++++++++++-- 1 file changed, 125 insertions(+), 15 deletions(-) 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 02b9f6f0b..e092ad39b 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 @@ -1,11 +1,19 @@ package com.csse3200.game.entities.factories; import com.badlogic.gdx.audio.Sound; +import com.badlogic.gdx.graphics.Texture; 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.HumanAnimationController; import com.csse3200.game.entities.Entity; import com.csse3200.game.extensions.GameExtension; 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.components.PhysicsMovementComponent; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.rendering.DebugRenderer; import com.csse3200.game.rendering.RenderService; @@ -13,27 +21,38 @@ import com.csse3200.game.services.ResourceService; import com.csse3200.game.services.ServiceLocator; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import java.util.ArrayList; +import java.util.Objects; + import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(GameExtension.class) class EngineerFactoryTest { - Entity target = new Entity(); - private String[] texture = { - "images/engineers/engineer.png" - }; + private String[] atlas = {"images/engineers/engineer.atlas"}; private static final String[] sounds = { "sounds/engineers/firing_auto.mp3" }; + private String[] animations = { + "idle_right", + "walk_left", + "walk_right", + "firing", + "hit", + "death" + };; + @BeforeEach void setUp() { GameTime gameTime = mock(GameTime.class); @@ -50,23 +69,114 @@ void setUp() { resourceService.loadAll(); } - @AfterEach - void tearDown() { + @Test + void createBaseHumanNPC() { + Entity human = EngineerFactory.createBaseHumanNPC(); + assertNotNull(human); + } + @Test + void testCreateEngineer() { + // Check engineer exists after creation + Entity engineer = EngineerFactory.createEngineer(); + assertNotNull(engineer, "Engineer entity should not be null after creation"); } @Test - void createEngineer() { + void testDeleteEngineer() { Entity engineer = EngineerFactory.createEngineer(); - engineer.setPosition(10f, 10f); - assertNotNull(engineer); - assert(!engineer.getFlagForDelete()); - assert engineer.getPosition().x == 10f && engineer.getPosition().y == 10f; + assertFalse(engineer.getFlagForDelete(), "Engineer flagForDelete should be false on creation"); + engineer.setFlagForDelete(true); + assertTrue(engineer.getFlagForDelete(), "Engineer getflagForDelete should return true after being set"); } @Test - void createBaseHumanNPC() { - Entity human = EngineerFactory.createBaseHumanNPC(); - assertNotNull(human); + void testEngineerPhysicsComponents() { + Entity engineer = EngineerFactory.createEngineer(); + assertNotNull(engineer.getComponent(PhysicsComponent.class), + "Engineer should have a PhysicsComponent"); + assertNotNull(engineer.getComponent(PhysicsMovementComponent.class), + "Engineer should have a PhysicsMovementComponent"); + } + + @Test + void testEngineerColliderAndHitboxComponents() { + Entity engineer = EngineerFactory.createEngineer(); + assertNotNull(engineer.getComponent(ColliderComponent.class), + "Engineer should have a ColliderComponent"); + assertNotNull(engineer.getComponent(HitboxComponent.class), + "Engineer should have a HitBoxComponent"); + } + + @Test + void testEngineerTouchAttackAndCombatStatsComponents() { + Entity engineer = EngineerFactory.createEngineer(); + assertNotNull(engineer.getComponent(TouchAttackComponent.class), + "Engineer should have a TouchAttackComponent"); + assertNotNull(engineer.getComponent(CombatStatsComponent.class), + "Engineer should have a CombatStatsComponent"); + } + + @Test + void testEngineerAnimationAndAITaskComponents() { + Entity engineer = EngineerFactory.createEngineer(); + assertNotNull(engineer.getComponent(HumanAnimationController.class), + "Engineer should have a HumanAnimationController"); + assertNotNull(engineer.getComponent(AITaskComponent.class), + "Engineer should have an AITaskComponent"); + assertNotNull(engineer.getComponent(AnimationRenderComponent.class), + "Engineer should have an AnimationRenderComponent"); + } + + @Test + void testEngineerAnimations() { + Entity engineer = EngineerFactory.createEngineer(); + for (String animation : this.animations) { + assertTrue(engineer.getComponent(AnimationRenderComponent.class).hasAnimation(animation), + ("Engineer AnimationRenderComponent should contain animation [" + animation + "]")); + } + for (String animation : this.animations) { + engineer.getComponent(AnimationRenderComponent.class).startAnimation(animation); + assert(Objects.equals(engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), animation)); + } + } + + @Test + void testEngineerConfig() { + Entity engineer = EngineerFactory.createEngineer(); + assert(engineer.getComponent(CombatStatsComponent.class).getHealth() == 100); + assert(engineer.getComponent(CombatStatsComponent.class).getBaseAttack() == 5); + } + + /** + * Adapted from TowerFactoryTest testAttackerCollisionWithWall by MohamadDab11 + */ + @Test + void testEngineerCollisions() { + Entity engineer = EngineerFactory.createEngineer(); + + Entity attacker = createAttacker(engineer.getComponent(HitboxComponent.class).getLayer()); + + engineer.setPosition(10f,10f); + attacker.setPosition(10f,10f); + engineer.create(); + + assertEquals(100, engineer.getComponent(CombatStatsComponent.class).getHealth()); + + ServiceLocator.getPhysicsService().getPhysics().update(); + + assertEquals(90, engineer.getComponent(CombatStatsComponent.class).getHealth()); + + } + + Entity createAttacker(short targetLayer) { + Entity entity = + new Entity() + .addComponent(new TouchAttackComponent(targetLayer)) + .addComponent(new CombatStatsComponent(0, 10)) + .addComponent(new PhysicsComponent()) + .addComponent(new HitboxComponent()); + entity.create(); + return entity; } -} \ No newline at end of file +} From a81143297c890515e66a31722a225d67ac09e0f0 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Fri, 8 Sep 2023 11:15:07 +1000 Subject: [PATCH 34/40] fixed merge conflicts from merging main into Team-2-engineers-ahmad --- .../com/csse3200/game/components/tasks/EngineerCombatTask.java | 3 ++- .../main/com/csse3200/game/components/tasks/MobAttackTask.java | 2 +- .../com/csse3200/game/components/tasks/TowerCombatTask.java | 2 +- .../game/entities/factories/ProjectileFactoryTest.java | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java index ac248ac59..a8af75a56 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java @@ -3,6 +3,7 @@ import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.DefaultTask; import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.ProjectileFactory; import com.csse3200.game.physics.PhysicsEngine; @@ -132,7 +133,7 @@ public void updateEngineerState() { if (shotsFired <= 10) { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state - Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), + Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.HUMANS, new Vector2(100, owner.getEntity().getPosition().y), // fetchTarget(), new Vector2(4f, 4f)); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java b/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java index aaba29da7..e6a83c610 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java @@ -126,7 +126,7 @@ public void updateMobState() { mobState = STATE.STOW; } else { owner.getEntity().getEvents().trigger(FIRING); - Entity newProjectile = ProjectileFactory.createMobBall(PhysicsLayer.PLAYER, new Vector2(0, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + Entity newProjectile = ProjectileFactory.createMobBall(PhysicsLayer.HUMANS, new Vector2(0, owner.getEntity().getPosition().y), new Vector2(2f,2f)); newProjectile.setPosition((float) (owner.getEntity().getPosition().x), (float) (owner.getEntity().getPosition().y)); newProjectile.setScale(-1f, 0.5f); ServiceLocator.getEntityService().register(newProjectile); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 82cde5225..327da9fae 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -112,7 +112,7 @@ public void updateTowerState() { } else { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state - Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.PLAYER, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.HUMANS, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); // * TEMPORARYYYYYYY // Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); diff --git a/source/core/src/test/com/csse3200/game/entities/factories/ProjectileFactoryTest.java b/source/core/src/test/com/csse3200/game/entities/factories/ProjectileFactoryTest.java index 040278787..925f44e03 100644 --- a/source/core/src/test/com/csse3200/game/entities/factories/ProjectileFactoryTest.java +++ b/source/core/src/test/com/csse3200/game/entities/factories/ProjectileFactoryTest.java @@ -56,7 +56,7 @@ public void setUp() { // ServiceLocator.getResourceService() // .getAsset("images/projectiles/basic_projectile.atlas", TextureAtlas.class); Vector2 destination = new Vector2(0.1f, 0.1f); - short targetLayer = PhysicsLayer.PLAYER; + short targetLayer = PhysicsLayer.HUMANS; Vector2 speed = new Vector2(2f, 2f); projectile = ProjectileFactory.createBaseProjectile(targetLayer, destination, speed); } From ca8e48ed210358d8891e8496012b6fd485128e20 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Fri, 8 Sep 2023 11:34:22 +1000 Subject: [PATCH 35/40] minor fixes to human combat tasks to fix missing projectiles --- .../src/main/com/csse3200/game/areas/ForestGameArea.java | 3 ++- .../game/components/player/HumanAnimationController.java | 8 ++++---- .../game/components/tasks/EngineerCombatTask.java | 2 +- .../csse3200/game/components/tasks/TowerCombatTask.java | 4 ++-- .../game/entities/factories/EngineerFactoryTest.java | 3 ++- 5 files changed, 11 insertions(+), 9 deletions(-) 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 1340d60ed..716c0b222 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -113,7 +113,8 @@ public class ForestGameArea extends GameArea { "sounds/towers/gun_shot_trimmed.mp3", "sounds/towers/deploy.mp3", "sounds/towers/stow.mp3", - "sounds/engineers/firing_auto.mp3" + "sounds/engineers/firing_auto.mp3", + "sounds/engineers/firing_single.mp3" }; private static final String backgroundMusic = "sounds/background/Sci-Fi1.ogg"; private static final String[] forestMusic = {backgroundMusic}; 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 990a4f96f..267745c84 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 @@ -29,7 +29,7 @@ public class HumanAnimationController extends Component { // Sound effects constants // private static final String RUN_SFX = ""; private static final String FIRE_AUTO_SFX = "sounds/engineers/firing_auto.mp3"; -// private static final String FIRE_SINGLE_SFX = "sounds/engineers/firing_single.mp3"; + private static final String FIRE_SINGLE_SFX = "sounds/engineers/firing_single.mp3"; // private static final String HIT_SFX = ""; // private static final String DEATH_SFX = ""; @@ -38,8 +38,8 @@ public class HumanAnimationController extends Component { // RUN_SFX, Sound.class); // Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( // FIRE_AUTO_SFX, Sound.class); -// Sound fireSingleSound = ServiceLocator.getResourceService().getAsset( -// FIRE_SINGLE_SFX, Sound.class); + Sound fireSingleSound = ServiceLocator.getResourceService().getAsset( + FIRE_SINGLE_SFX, Sound.class); // Sound hitSound = ServiceLocator.getResourceService().getAsset( // HIT_SFX, Sound.class); // Sound deathSound = ServiceLocator.getResourceService().getAsset( @@ -76,7 +76,7 @@ void animateRightWalk() { void animateFiring() { animator.startAnimation(FIRE_ANIM); -// fireAutoSound.play(); + fireSingleSound.play(); } void animateHit() { diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java index a8af75a56..7019ccb49 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java @@ -133,7 +133,7 @@ public void updateEngineerState() { if (shotsFired <= 10) { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state - Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.HUMANS, + Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), // fetchTarget(), new Vector2(4f, 4f)); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 327da9fae..752a67859 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -112,12 +112,12 @@ public void updateTowerState() { } else { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state - Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.HUMANS, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); // * TEMPORARYYYYYYY // Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.75)); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); ServiceLocator.getEntityService().register(newProjectile); } } 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 e092ad39b..1e940208a 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 @@ -41,7 +41,8 @@ class EngineerFactoryTest { private String[] atlas = {"images/engineers/engineer.atlas"}; private static final String[] sounds = { - "sounds/engineers/firing_auto.mp3" + "sounds/engineers/firing_auto.mp3", + "sounds/engineers/firing_single.mp3" }; private String[] animations = { From 6c17533b3846b4aa55a5dcbea726b971aaf06a88 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Fri, 8 Sep 2023 13:24:01 +1000 Subject: [PATCH 36/40] added more engineer animations --- .../assets/images/engineers/engineer.atlas | 308 ++++++++++++------ .../core/assets/images/engineers/engineer.png | Bin 21189 -> 26087 bytes 2 files changed, 210 insertions(+), 98 deletions(-) diff --git a/source/core/assets/images/engineers/engineer.atlas b/source/core/assets/images/engineers/engineer.atlas index 4db973b8c..bb4fccbe2 100644 --- a/source/core/assets/images/engineers/engineer.atlas +++ b/source/core/assets/images/engineers/engineer.atlas @@ -1,272 +1,384 @@ engineer.png -size: 512, 256 +size: 2048, 128 format: RGBA8888 filter: Nearest, Nearest repeat: none -firing +death rotate: false - xy: 25, 190 - size: 52, 31 - orig: 52, 31 + xy: 503, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 6 +death + rotate: false + xy: 902, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 3 +death + rotate: false + xy: 1358, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 5 +death + rotate: false + xy: 1814, 79 + size: 52, 32 + orig: 32, 32 offset: 0, 0 index: 2 -firing +death rotate: false - xy: 25, 154 - size: 52, 31 - orig: 52, 31 + xy: 454, 42 + size: 52, 32 + orig: 32, 32 offset: 0, 0 index: 4 -firing +death rotate: false - xy: 102, 190 - size: 52, 31 - orig: 52, 31 + xy: 796, 42 + size: 52, 32 + orig: 32, 32 offset: 0, 0 index: 1 -firing +firing_auto rotate: false - xy: 25, 118 + xy: 25, 80 size: 52, 31 orig: 52, 31 offset: 0, 0 - index: 3 -death + index: 2 +firing_auto rotate: false - xy: 25, 81 + xy: 25, 44 size: 52, 31 - orig: 32, 32 + orig: 52, 31 offset: 0, 0 - index: 6 -death + index: 4 +firing_auto rotate: false - xy: 350, 189 + xy: 102, 80 size: 52, 31 - orig: 32, 32 + orig: 52, 31 offset: 0, 0 - index: 3 -death + index: 1 +firing_auto rotate: false - xy: 330, 152 + xy: 25, 8 size: 52, 31 - orig: 32, 32 + orig: 52, 31 offset: 0, 0 - index: 5 -death + index: 3 +firing_single rotate: false - xy: 82, 79 - size: 52, 31 - orig: 32, 32 + xy: 102, 42 + size: 45, 33 + orig: 45, 33 offset: 0, 0 index: 2 -death +firing_single rotate: false - xy: 253, 78 - size: 52, 31 - orig: 32, 32 + xy: 179, 78 + size: 45, 33 + orig: 45, 33 offset: 0, 0 index: 4 -death +firing_single rotate: false - xy: 367, 41 - size: 52, 31 - orig: 32, 32 + xy: 249, 78 + size: 45, 33 + orig: 45, 33 offset: 0, 0 index: 1 +firing_single + rotate: false + xy: 319, 78 + size: 45, 33 + orig: 45, 33 + offset: 0, 0 + index: 3 hit rotate: false - xy: 25, 7 - size: 52, 31 + xy: 560, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 3 hit rotate: false - xy: 159, 115 - size: 52, 31 + xy: 1415, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 2 hit rotate: false - xy: 253, 41 - size: 52, 31 + xy: 511, 42 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 1 idle_left rotate: false - xy: 179, 189 - size: 52, 31 + xy: 674, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 2 idle_left rotate: false - xy: 216, 152 - size: 52, 31 + xy: 1130, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 4 idle_left rotate: false - xy: 216, 115 - size: 52, 31 + xy: 1529, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 1 idle_left rotate: false - xy: 139, 41 - size: 52, 31 + xy: 102, 5 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 3 idle_right rotate: false - xy: 159, 152 - size: 52, 31 + xy: 1016, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 2 idle_right rotate: false - xy: 273, 115 - size: 52, 31 + xy: 1586, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 4 +default + rotate: false + xy: 1928, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 idle_right rotate: false - xy: 82, 5 - size: 52, 31 + xy: 1928, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 1 idle_right rotate: false - xy: 310, 78 - size: 52, 31 + xy: 568, 42 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 3 -default +prep rotate: false - xy: 216, 115 - size: 52, 31 - orig: 32, 32 + xy: 343, 40 + size: 29, 33 + orig: 29, 33 + offset: 0, 0 + index: 4 +prep + rotate: false + xy: 853, 41 + size: 29, 33 + orig: 29, 33 offset: 0, 0 index: 1 +prep + rotate: false + xy: 907, 41 + size: 29, 33 + orig: 29, 33 + offset: 0, 0 + index: 3 +prep + rotate: false + xy: 961, 41 + size: 29, 33 + orig: 29, 33 + offset: 0, 0 + index: 2 walk_left rotate: false - xy: 102, 153 - size: 52, 31 + xy: 446, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 6 walk_left rotate: false - xy: 293, 189 - size: 52, 31 + xy: 845, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 3 walk_left rotate: false - xy: 407, 189 - size: 52, 31 + xy: 959, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 8 walk_left rotate: false - xy: 273, 152 - size: 52, 31 + xy: 1301, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 5 walk_left rotate: false - xy: 387, 115 - size: 52, 31 + xy: 1700, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 2 walk_left rotate: false - xy: 82, 42 - size: 52, 31 + xy: 1871, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 7 walk_left rotate: false - xy: 196, 78 - size: 52, 31 + xy: 397, 42 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 4 walk_left rotate: false - xy: 367, 78 - size: 52, 31 + xy: 739, 42 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 1 +walk_prep + rotate: false + xy: 389, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 8 +walk_prep + rotate: false + xy: 731, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 5 +walk_prep + rotate: false + xy: 1073, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 2 +walk_prep + rotate: false + xy: 1244, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 7 +walk_prep + rotate: false + xy: 1643, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 4 +walk_prep + rotate: false + xy: 1985, 79 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 1 +walk_prep + rotate: false + xy: 229, 41 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 6 +walk_prep + rotate: false + xy: 625, 42 + size: 52, 32 + orig: 32, 32 + offset: 0, 0 + index: 3 walk_right rotate: false - xy: 25, 44 - size: 52, 31 + xy: 617, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 2 walk_right rotate: false - xy: 236, 189 - size: 52, 31 + xy: 788, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 7 walk_right rotate: false - xy: 102, 116 - size: 52, 31 + xy: 1187, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 4 walk_right rotate: false - xy: 387, 152 - size: 52, 31 + xy: 1472, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 1 walk_right rotate: false - xy: 330, 115 - size: 52, 31 + xy: 1757, 79 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 6 walk_right rotate: false - xy: 139, 78 - size: 52, 31 + xy: 172, 41 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 3 walk_right rotate: false - xy: 196, 41 - size: 52, 31 + xy: 286, 41 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 8 walk_right rotate: false - xy: 310, 41 - size: 52, 31 + xy: 682, 42 + size: 52, 32 orig: 32, 32 offset: 0, 0 index: 5 diff --git a/source/core/assets/images/engineers/engineer.png b/source/core/assets/images/engineers/engineer.png index acc36d01d04eb7a4c253f91e752f722553b0724d..c2df5eddd038e9d7c87ef5328c888bd8df348d33 100644 GIT binary patch literal 26087 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

R+JmTcas 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 Cb`S0V From d198eeb8490c924dd2238a0b5ea9058110e323c5 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Fri, 8 Sep 2023 13:25:13 +1000 Subject: [PATCH 37/40] updated engineer classes to use a new animation and event types --- .../player/HumanAnimationController.java | 34 +++++++++++++++---- .../components/tasks/EngineerCombatTask.java | 15 ++++---- .../entities/factories/EngineerFactory.java | 5 ++- .../factories/EngineerFactoryTest.java | 5 ++- 4 files changed, 43 insertions(+), 16 deletions(-) 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 267745c84..78d635895 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 @@ -15,7 +15,10 @@ public class HumanAnimationController extends Component { private static final String IDLER = "idleRight"; private static final String WALKL = "walkLeftStart"; private static final String WALKR = "walkRightStart"; - private static final String FIRING = "firingStart"; + private static final String WALK_PREP = "walkPrepStart"; + private static final String PREP = "prepStart"; + private static final String FIRING_AUTO = "firingAutoStart"; + private static final String FIRING_SINGLE = "firingSingleStart"; private static final String HIT = "hitStart"; private static final String DEATH = "deathStart"; // Animation name constants @@ -23,7 +26,9 @@ public class HumanAnimationController extends Component { private static final String IDLER_ANIM = "idle_right"; private static final String WALKL_ANIM = "walk_left"; private static final String WALKR_ANIM = "walk_right"; - private static final String FIRE_ANIM = "firing"; + private static final String WALK_PREP_ANIM = "walk_prep"; + private static final String FIRE_AUTO_ANIM = "firing_auto"; + private static final String FIRE_SINGLE_ANIM = "firing_single"; private static final String HIT_ANIM = "hit"; private static final String DEATH_ANIM = "death"; // Sound effects constants @@ -36,8 +41,8 @@ public class HumanAnimationController extends Component { AnimationRenderComponent animator; // Sound runSound = ServiceLocator.getResourceService().getAsset( // RUN_SFX, Sound.class); -// Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( -// FIRE_AUTO_SFX, Sound.class); + Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( + FIRE_AUTO_SFX, Sound.class); Sound fireSingleSound = ServiceLocator.getResourceService().getAsset( FIRE_SINGLE_SFX, Sound.class); // Sound hitSound = ServiceLocator.getResourceService().getAsset( @@ -53,7 +58,10 @@ public void create() { entity.getEvents().addListener(IDLER, this::animateIdleRight); entity.getEvents().addListener(WALKL, this::animateLeftWalk); entity.getEvents().addListener(WALKR, this::animateRightWalk); - entity.getEvents().addListener(FIRING, this::animateFiring); + 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(HIT, this::animateHit); entity.getEvents().addListener(DEATH, this::animateDeath); } @@ -74,10 +82,22 @@ void animateRightWalk() { // runSound.play(); } - void animateFiring() { - animator.startAnimation(FIRE_ANIM); + void animatePrepWalk() { + animator.startAnimation(WALK_PREP_ANIM); + } + + void animateSingleFiring() { + animator.startAnimation(FIRE_SINGLE_ANIM); fireSingleSound.play(); } + void animateFiring() { + animator.startAnimation(FIRE_AUTO_ANIM); + fireAutoSound.play(); + } + + void animatePrep() { + animator.startAnimation(PREP); + } void animateHit() { animator.startAnimation(HIT_ANIM); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java index 7019ccb49..9393c3e04 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java @@ -27,7 +27,7 @@ public class EngineerCombatTask extends DefaultTask implements PriorityTask { // Animation event names for the Engineer's state machine. private static final String STOW = ""; private static final String DEPLOY = ""; - private static final String FIRING = "firingStart"; + private static final String FIRING = "firingSingleStart"; private static final String IDLE_LEFT = "idleLeft"; private static final String IDLE_RIGHT = "idleRight"; private static final String DYING = "deathStart"; @@ -109,15 +109,13 @@ public void updateEngineerState() { case IDLE_RIGHT -> { // targets detected in idle mode - start deployment if (isTargetVisible()) { - owner.getEntity().getEvents().trigger(FIRING); - engineerState = STATE.FIRING; + combatState(); } } case DEPLOY -> { // currently deploying, if (isTargetVisible()) { - owner.getEntity().getEvents().trigger(FIRING); - engineerState = STATE.FIRING; + combatState(); } else { owner.getEntity().getEvents().trigger(STOW); engineerState = STATE.STOW; @@ -126,7 +124,6 @@ public void updateEngineerState() { case FIRING -> { // targets gone - stop firing if (!isTargetVisible()) { - owner.getEntity().getEvents().trigger(IDLE_RIGHT); engineerState = STATE.IDLE_RIGHT; } else { @@ -135,7 +132,6 @@ public void updateEngineerState() { // this might be changed to an event which gets triggered everytime the tower enters the firing state Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), -// fetchTarget(), new Vector2(4f, 4f)); newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); ServiceLocator.getEntityService().register(newProjectile); @@ -153,6 +149,11 @@ public void updateEngineerState() { } } } + + private void combatState() { + owner.getEntity().getEvents().trigger(FIRING); + engineerState = STATE.FIRING; + } /** * For stopping the running task */ diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 8f5972e85..3f0edc0a5 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -54,8 +54,11 @@ public static Entity createEngineer() { new TextureAtlas("images/engineers/engineer.atlas")); animator.addAnimation("walk_left", 0.2f, Animation.PlayMode.LOOP); animator.addAnimation("walk_right", 0.2f, Animation.PlayMode.LOOP); + animator.addAnimation("walk_prep", 0.2f, Animation.PlayMode.LOOP); animator.addAnimation("idle_right", 0.2f, Animation.PlayMode.LOOP); - animator.addAnimation("firing", 0.05f, Animation.PlayMode.NORMAL); + animator.addAnimation("firing_auto", 0.05f, Animation.PlayMode.NORMAL); + animator.addAnimation("firing_single", 0.05f, Animation.PlayMode.NORMAL); + animator.addAnimation("prep", 0.05f, Animation.PlayMode.NORMAL); animator.addAnimation("hit", 0.01f, Animation.PlayMode.NORMAL); animator.addAnimation("death", 0.1f, Animation.PlayMode.NORMAL); 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 1e940208a..7be8641f4 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 @@ -49,7 +49,10 @@ class EngineerFactoryTest { "idle_right", "walk_left", "walk_right", - "firing", + "walk_prep", + "prep", + "firing_auto", + "firing_single", "hit", "death" };; From b2d74629c8d3e8a1164ef90a433ff06e126011d2 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Fri, 8 Sep 2023 13:25:41 +1000 Subject: [PATCH 38/40] updated ForestGameArea to spawn a fixed number of engineers --- .../csse3200/game/areas/ForestGameArea.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) 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 716c0b222..50d2a5c52 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -493,13 +493,16 @@ private void spawnIncome() { } private void spawnEngineer() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = new GridPoint2(5, terrain.getMapBounds(0).sub(2, 2).y); - GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - - Entity engineer = EngineerFactory.createEngineer(); - spawnEntityAt(engineer, randomPos, true, true); - } - + for (int i = 0; i < terrain.getMapBounds(0).x; i += 3) { + Entity engineer = EngineerFactory.createEngineer(); + spawnEntityAt(engineer, new GridPoint2(2, i), true, true); + } +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = new GridPoint2(5, terrain.getMapBounds(0).sub(2, 2).y); +// GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); +// +// Entity engineer = EngineerFactory.createEngineer(); +// spawnEntityAt(engineer, randomPos, true, true); + } } \ No newline at end of file From 7772795586718f3b087c33a9888878c4a75c2cfe Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Fri, 8 Sep 2023 13:37:07 +1000 Subject: [PATCH 39/40] fixes to engineer firing animations --- .../assets/images/engineers/engineer.atlas | 23 ++++++++++++------- .../csse3200/game/areas/ForestGameArea.java | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/source/core/assets/images/engineers/engineer.atlas b/source/core/assets/images/engineers/engineer.atlas index bb4fccbe2..2b98261bd 100644 --- a/source/core/assets/images/engineers/engineer.atlas +++ b/source/core/assets/images/engineers/engineer.atlas @@ -77,31 +77,38 @@ firing_auto firing_single rotate: false xy: 102, 42 - size: 45, 33 + size: 52, 33 orig: 45, 33 offset: 0, 0 index: 2 firing_single rotate: false xy: 179, 78 - size: 45, 33 + size: 52, 33 orig: 45, 33 offset: 0, 0 index: 4 firing_single rotate: false xy: 249, 78 - size: 45, 33 + size: 52, 33 orig: 45, 33 offset: 0, 0 index: 1 firing_single rotate: false xy: 319, 78 - size: 45, 33 + size: 52, 33 orig: 45, 33 offset: 0, 0 index: 3 +firing_single + rotate: false + xy: 343, 40 + size: 52, 33 + orig: 29, 33 + offset: 0, 0 + index: 5 hit rotate: false xy: 560, 79 @@ -189,28 +196,28 @@ idle_right prep rotate: false xy: 343, 40 - size: 29, 33 + size: 52, 33 orig: 29, 33 offset: 0, 0 index: 4 prep rotate: false xy: 853, 41 - size: 29, 33 + size: 52, 33 orig: 29, 33 offset: 0, 0 index: 1 prep rotate: false xy: 907, 41 - size: 29, 33 + size: 52, 33 orig: 29, 33 offset: 0, 0 index: 3 prep rotate: false xy: 961, 41 - size: 29, 33 + size: 52, 33 orig: 29, 33 offset: 0, 0 index: 2 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 50d2a5c52..698c3c282 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -496,7 +496,7 @@ private void spawnEngineer() { for (int i = 0; i < terrain.getMapBounds(0).x; i += 3) { Entity engineer = EngineerFactory.createEngineer(); - spawnEntityAt(engineer, new GridPoint2(2, i), true, true); + spawnEntityAt(engineer, new GridPoint2(1, i), true, true); } // GridPoint2 minPos = new GridPoint2(0, 0); // GridPoint2 maxPos = new GridPoint2(5, terrain.getMapBounds(0).sub(2, 2).y); From 589a59b7fb3f950f6143197ea7467deb22a59d27 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Fri, 8 Sep 2023 15:42:23 +1000 Subject: [PATCH 40/40] updated comments --- .../player/HumanAnimationController.java | 51 ++++++++++++++----- .../components/tasks/EngineerCombatTask.java | 25 +++++++-- 2 files changed, 61 insertions(+), 15 deletions(-) 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 78d635895..e65fc8763 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 @@ -32,24 +32,19 @@ public class HumanAnimationController extends Component { private static final String HIT_ANIM = "hit"; private static final String DEATH_ANIM = "death"; // Sound effects constants -// private static final String RUN_SFX = ""; private static final String FIRE_AUTO_SFX = "sounds/engineers/firing_auto.mp3"; private static final String FIRE_SINGLE_SFX = "sounds/engineers/firing_single.mp3"; -// private static final String HIT_SFX = ""; -// private static final String DEATH_SFX = ""; AnimationRenderComponent animator; -// Sound runSound = ServiceLocator.getResourceService().getAsset( -// RUN_SFX, Sound.class); Sound fireAutoSound = ServiceLocator.getResourceService().getAsset( FIRE_AUTO_SFX, Sound.class); Sound fireSingleSound = ServiceLocator.getResourceService().getAsset( FIRE_SINGLE_SFX, Sound.class); -// Sound hitSound = ServiceLocator.getResourceService().getAsset( -// HIT_SFX, Sound.class); -// Sound deathSound = ServiceLocator.getResourceService().getAsset( -// HIT_SFX, Sound.class); + /** + * Instantiates a HumanAnimationController and adds all the event listeners for the + * Human entity - Just engineers at this stage. + */ @Override public void create() { super.create(); @@ -66,46 +61,78 @@ public void create() { entity.getEvents().addListener(DEATH, this::animateDeath); } + /** + * Callback that starts the idle animation facing left + */ void animateIdleLeft() { animator.startAnimation(IDLEL_ANIM); } + + /** + * Callback that starts the idle animation facing right + */ void animateIdleRight() { animator.startAnimation(IDLER_ANIM); } + /** + * Callback that starts the walk animation for left movement + */ void animateLeftWalk() { animator.startAnimation(WALKL_ANIM); // runSound.play(); } + + /** + * Callback that starts the walk animation for right movement + */ void animateRightWalk() { animator.startAnimation(WALKR_ANIM); -// runSound.play(); } + /** + * Callback that starts the walk animation in the 'prepared' state, i.e., weapon up and ready to fight - currently + * unused, but intended to be incorporated as engineer functionality expands + */ void animatePrepWalk() { animator.startAnimation(WALK_PREP_ANIM); } + /** + * Callback that starts the shoot animation in single fire mode, and plays the single fire sound + */ void animateSingleFiring() { animator.startAnimation(FIRE_SINGLE_ANIM); fireSingleSound.play(); } + + /** + * 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() { animator.startAnimation(FIRE_AUTO_ANIM); fireAutoSound.play(); } + /** + * Callback that starts the 'prep' animation, i.e., raising weapon in preparation for firing + */ void animatePrep() { animator.startAnimation(PREP); } + /** + * Callback that starts the 'hit' animation when engineer is damaged + */ void animateHit() { animator.startAnimation(HIT_ANIM); -// hitSound.play(); } + /** + * Callback that starts the 'death' animation when the engineer entity's health reaches zero. + */ void animateDeath() { animator.startAnimation(DEATH_ANIM); -// deathSound.play(); } } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java index 9393c3e04..40e91edea 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java @@ -22,6 +22,7 @@ public class EngineerCombatTask extends DefaultTask implements PriorityTask { 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 TARGET = PhysicsLayer.NPC; // The type of targets that the Engineer will detect. // Animation event names for the Engineer's state machine. @@ -150,6 +151,9 @@ public void updateEngineerState() { } } + /** + * Puts the engineerCombatTask state into combat mode + */ private void combatState() { owner.getEntity().getEvents().trigger(FIRING); engineerState = STATE.FIRING; @@ -162,14 +166,20 @@ public void stop() { super.stop(); } + /** + * 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 isTargetVisible() ? 3 : 0; + 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 + * 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 engineer entity to detect mobs in adjacent lanes. + * @return true if a target is detected, false otherwise */ public boolean isTargetVisible() { // If there is an obstacle in the path to the max range point, mobs visible. @@ -184,10 +194,19 @@ public boolean isTargetVisible() { 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);