From 6fd9e593ae0b182a76a08f01720f460553e4da2f Mon Sep 17 00:00:00 2001 From: bojyyy <140468434+bojyyy@users.noreply.github.com> Date: Sun, 15 Oct 2023 13:01:53 +1000 Subject: [PATCH 01/20] Removes outdated SpawnWaveTask --- .../game/components/tasks/SpawnWaveTask.java | 37 ------------------ .../components/tasks/SpawnWaveTaskTest.java | 39 ------------------- 2 files changed, 76 deletions(-) delete mode 100644 source/core/src/main/com/csse3200/game/components/tasks/SpawnWaveTask.java delete mode 100644 source/core/src/test/com/csse3200/game/components/tasks/SpawnWaveTaskTest.java diff --git a/source/core/src/main/com/csse3200/game/components/tasks/SpawnWaveTask.java b/source/core/src/main/com/csse3200/game/components/tasks/SpawnWaveTask.java deleted file mode 100644 index d6c4a3d85..000000000 --- a/source/core/src/main/com/csse3200/game/components/tasks/SpawnWaveTask.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.csse3200.game.components.tasks; - -import com.csse3200.game.ai.tasks.DefaultTask; -import com.csse3200.game.ai.tasks.PriorityTask; -import com.csse3200.game.services.GameTime; -import com.csse3200.game.services.ServiceLocator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SpawnWaveTask extends DefaultTask implements PriorityTask { - private static final Logger logger = LoggerFactory.getLogger(SpawnWaveTask.class); - private final GameTime globalTime; - private long endTime = 0; - private final int SPAWNING_INTERVAL = 10; - public SpawnWaveTask() { - this.globalTime = ServiceLocator.getTimeSource(); - } - - @Override - public int getPriority() { - return 10; // High priority task - } - - @Override - public void start() { - super.start(); - endTime = globalTime.getTime() + (SPAWNING_INTERVAL * 1000); - } - - @Override - public void update() { - if (globalTime.getTime() >= endTime) { - this.owner.getEntity().getEvents().trigger("spawnWave"); - endTime = globalTime.getTime() + (SPAWNING_INTERVAL * 1000L); // reset end time - } - } -} diff --git a/source/core/src/test/com/csse3200/game/components/tasks/SpawnWaveTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/SpawnWaveTaskTest.java deleted file mode 100644 index 58fb2ae9b..000000000 --- a/source/core/src/test/com/csse3200/game/components/tasks/SpawnWaveTaskTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.csse3200.game.components.tasks; - -import com.csse3200.game.ai.tasks.AITaskComponent; -import com.csse3200.game.entities.Entity; -import com.csse3200.game.events.listeners.EventListener0; -import com.csse3200.game.extensions.GameExtension; -import com.csse3200.game.physics.components.PhysicsMovementComponent; -import com.csse3200.game.services.GameTime; -import com.csse3200.game.services.ServiceLocator; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.mockito.Mockito.*; - -@ExtendWith(GameExtension.class) -@ExtendWith(MockitoExtension.class) -class SpawnWaveTaskTest { - - @Test - void shouldTriggerSpawning() { - GameTime time = mock(GameTime.class); - when(time.getTime()).thenReturn(11000L); - ServiceLocator.registerTimeSource(time); - SpawnWaveTask waveTask = new SpawnWaveTask(); - - AITaskComponent aiTaskComponent = new AITaskComponent().addTask(waveTask); - Entity entity = new Entity().addComponent(aiTaskComponent).addComponent(new PhysicsMovementComponent()); - entity.create(); - - // Register callbacks - EventListener0 callback = mock(EventListener0.class); - entity.getEvents().addListener("spawnWave", callback); - - waveTask.update(); - - verify(callback).handle(); - } -} \ No newline at end of file From fced5a7f2ff033874031224125aff6780b22eb65 Mon Sep 17 00:00:00 2001 From: bojyyy <140468434+bojyyy@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:48:00 +1000 Subject: [PATCH 02/20] Fixes a bug with the mob counter going negative due to splitting mobs --- .../game/components/npc/SplitMoblings.java | 3 +-- .../game/entities/factories/NPCFactory.java | 25 ------------------- .../game/entities/factories/WaveFactory.java | 4 +-- .../game/components/SplitMoblingsTest.java | 2 +- 4 files changed, 4 insertions(+), 30 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java b/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java index 5896c2f10..90a390e8d 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java +++ b/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java @@ -167,8 +167,7 @@ public void spawnAdditionalMob(float positionX, float positionY, ServiceLocator.getEntityService().register(entityType); - // ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); - //ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); + ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); } /** 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 1ceb66b31..4d9f0d554 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 @@ -73,31 +73,6 @@ public static Entity createGhost() { return ghost; } - /** - * Creates a ghost king entity. - * - * @return entity - */ - public static Entity createGhostKing() { - Entity ghostKing = createMeleeBaseNPC(); - GhostKingConfig config = configs.ghostKing; - - AnimationRenderComponent animator = - new AnimationRenderComponent( - ServiceLocator.getResourceService() - .getAsset("images/ghostKing.atlas", TextureAtlas.class)); - animator.addAnimation("float", 0.2f, Animation.PlayMode.LOOP); - animator.addAnimation("angry_float", 0.2f, Animation.PlayMode.LOOP); - - ghostKing - .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) - .addComponent(animator) - .addComponent(new GhostAnimationController()); - - ghostKing.getComponent(AnimationRenderComponent.class).scaleEntity(); - return ghostKing; - } - /** * Creates a fire worm entity. * diff --git a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java index c0526e42b..112926f93 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java @@ -55,7 +55,7 @@ public class WaveFactory { )); private static final ArrayList> lvl3Structure = new ArrayList<>(Arrays.asList( - new ArrayList<>(Arrays.asList("Necromancer" + new ArrayList<>(Arrays.asList("SplittingRocky" )), new ArrayList<>(Arrays.asList("Necromancer", "DodgingDragon" )), new ArrayList<>(Arrays.asList("Necromancer", "FireWorm" )), new ArrayList<>(Arrays.asList("Necromancer", "FireWorm" @@ -63,7 +63,7 @@ public class WaveFactory { )), new ArrayList<>(Arrays.asList("DodgingDragon", "FireWorm" )), new ArrayList<>(Arrays.asList("DodgingDragon", "Necromancer" )), new ArrayList<>(Arrays.asList("FireWorm", "Necromancer" - )), new ArrayList<>(Arrays.asList("DeflectFireWiza","SplittingRocky", "Necromancer" + )), new ArrayList<>(Arrays.asList("DeflectFireWizard","SplittingRocky", "Necromancer" )), new ArrayList<>(Arrays.asList("DodgingDragon", "DeflectFireWizard", "SplittingRocky", "Necromancer" )), new ArrayList<>(Arrays.asList("FireWorm", "DeflectWizard", "DodgingDragon" )), new ArrayList<>(Arrays.asList("FireWorm", "DeflectWizard", "Necromancer" diff --git a/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java b/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java index a28f836a1..ec985954d 100644 --- a/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java +++ b/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java @@ -66,7 +66,7 @@ public void setUp() { resourceService.loadTextureAtlases(atlas); resourceService.loadAll(); - WaveService waveService = new WaveService(); + WaveService waveService = mock(WaveService.class); ServiceLocator.registerWaveService(waveService); baseMob = createSplitMob(BASE_AMOUNT); From 5458c3db52106afebc645c6f00d57ae4eb9747e9 Mon Sep 17 00:00:00 2001 From: bojyyy <140468434+bojyyy@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:56:28 +1000 Subject: [PATCH 03/20] Reverts change to wave structure done for testing purposes --- .../main/com/csse3200/game/entities/factories/WaveFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java index 112926f93..9fb9b16e1 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java @@ -55,7 +55,7 @@ public class WaveFactory { )); private static final ArrayList> lvl3Structure = new ArrayList<>(Arrays.asList( - new ArrayList<>(Arrays.asList("SplittingRocky" + new ArrayList<>(Arrays.asList("Necromancer" )), new ArrayList<>(Arrays.asList("Necromancer", "DodgingDragon" )), new ArrayList<>(Arrays.asList("Necromancer", "FireWorm" )), new ArrayList<>(Arrays.asList("Necromancer", "FireWorm" From ab5ea49a82145f525b557b2c74d0729b5f668c5c Mon Sep 17 00:00:00 2001 From: bojyyy <140468434+bojyyy@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:05:51 +1000 Subject: [PATCH 04/20] Fixes a bug with the IceBaby boss where the mob counter was not updating when it spawned WaterSlimes --- .../com/csse3200/game/components/tasks/bosstask/IceBabyTask.java | 1 + 1 file changed, 1 insertion(+) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java index 80e703ebf..4b298c059 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java @@ -264,6 +264,7 @@ private void spawnMob() { Entity newMob = NPCFactory.createSplittingWaterSlime(80); newMob.setPosition((float) (iceBaby.getPosition().x + 0.5), (float) (iceBaby.getPosition().y + 0.5)); ServiceLocator.getEntityService().register(newMob); + ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); } /** From f2e7d04d6644fe39f009c2c167ed64c8692441ba Mon Sep 17 00:00:00 2001 From: Thivan W Date: Mon, 16 Oct 2023 19:26:39 +1000 Subject: [PATCH 05/20] Fixed tower scanner to match the enemies hitboxes --- .../com/csse3200/game/components/tasks/FireTowerCombatTask.java | 2 +- .../csse3200/game/components/tasks/PierceTowerCombatTask.java | 2 +- .../csse3200/game/components/tasks/RicochetTowerCombatTask.java | 2 +- .../com/csse3200/game/components/tasks/StunTowerCombatTask.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java index 13aa11a18..58a76a43b 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java @@ -67,7 +67,7 @@ public FireTowerCombatTask(int priority, float maxRange) { public void start() { super.start(); // get the tower coordinates - this.towerPosition = owner.getEntity().getCenterPosition(); + this.towerPosition = owner.getEntity().getCenterPosition().sub(0.125f,0.125f); this.maxRangePosition.set(towerPosition.x + maxRange, towerPosition.y); owner.getEntity().getEvents().addListener("addFireRate",this::changeFireRateInterval); //default to idle state diff --git a/source/core/src/main/com/csse3200/game/components/tasks/PierceTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/PierceTowerCombatTask.java index cdefde434..dffe53878 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/PierceTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/PierceTowerCombatTask.java @@ -63,7 +63,7 @@ public PierceTowerCombatTask(int priority, float maxRange) { public void start() { super.start(); // Get the tower coordinates - this.towerPosition = owner.getEntity().getCenterPosition(); + this.towerPosition = owner.getEntity().getCenterPosition().sub(0.25f, 0.25f); this.maxRangePosition.set(towerPosition.x + maxRange, towerPosition.y); // Set the default state to IDLE state owner.getEntity().getEvents().trigger(IDLE); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/RicochetTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/RicochetTowerCombatTask.java index 7fc3e5728..9e9aabc3e 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/RicochetTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/RicochetTowerCombatTask.java @@ -65,7 +65,7 @@ public RicochetTowerCombatTask(int priority, float maxRange) { public void start() { super.start(); // Get the tower coordinates - this.towerPosition = owner.getEntity().getCenterPosition(); + this.towerPosition = owner.getEntity().getCenterPosition().sub(0.25f, 0.25f); this.maxRangePosition.set(towerPosition.x + maxRange, towerPosition.y); // Set the default state to IDLE state owner.getEntity().getEvents().trigger(IDLE); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java index ec469b269..014903ffa 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java @@ -67,7 +67,7 @@ public StunTowerCombatTask(int priority, float maxRange) { public void start() { super.start(); //get the tower coordinates - this.towerPosition = owner.getEntity().getCenterPosition(); + this.towerPosition = owner.getEntity().getCenterPosition().sub(0.25f, 0.25f); this.maxRangePosition.set(towerPosition.x + maxRange, towerPosition.y); owner.getEntity().getEvents().addListener("addFireRate",this::changeFireRateInterval); //set the default state to IDLE state From a1545aff54030d0189bf3c4106823afa39d1539e Mon Sep 17 00:00:00 2001 From: max9753 Date: Mon, 16 Oct 2023 22:15:46 +1000 Subject: [PATCH 06/20] Removed logger imports in SpawnWaveTask and used String builder in LevelWaves as instructed by SonarCloud. --- .../csse3200/game/components/tasks/SpawnWaveTask.java | 2 -- .../game/components/tasks/waves/LevelWaves.java | 10 +++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/SpawnWaveTask.java b/source/core/src/main/com/csse3200/game/components/tasks/SpawnWaveTask.java index 912a2451b..201e3823f 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/SpawnWaveTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/SpawnWaveTask.java @@ -4,8 +4,6 @@ import com.csse3200.game.ai.tasks.PriorityTask; import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class SpawnWaveTask extends DefaultTask implements PriorityTask { diff --git a/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java b/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java index 7d9e0e731..cca65b2e5 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java @@ -124,11 +124,11 @@ public List getWaves() { @Override public String toString() { - String result = ""; - for (WaveClass wave : waves) { - result += wave.toString() + "\n"; - } - return result; + StringBuilder result = new StringBuilder(); + for (WaveClass wave : waves) { + result.append(wave.toString()).append("\n"); + } + return result.toString(); } } From f2c1398992f9794ebf4ec9541f062eb79fc2def2 Mon Sep 17 00:00:00 2001 From: max9753 Date: Mon, 16 Oct 2023 22:21:24 +1000 Subject: [PATCH 07/20] Removed unused mobIndex private variable. --- .../com/csse3200/game/components/tasks/waves/WaveClass.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveClass.java b/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveClass.java index 3e197601e..635834811 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveClass.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveClass.java @@ -1,15 +1,10 @@ package com.csse3200.game.components.tasks.waves; - -import com.csse3200.game.services.GameTime; - - import java.util.*; public class WaveClass { private HashMap entities; private List wave; - private int mobIndex; /** * Constructor for the WaveClass @@ -18,7 +13,6 @@ public class WaveClass { public WaveClass(HashMap entities) { this.entities = entities; this.wave = entitiesToWave(); - this.mobIndex = 0; } /** From c2b555592279a2567e23db89b7625a581bad70ff Mon Sep 17 00:00:00 2001 From: max9753 Date: Mon, 16 Oct 2023 22:27:09 +1000 Subject: [PATCH 08/20] Removed commented out code and bad comments etc in WaveTask. --- .../game/components/tasks/waves/WaveTask.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveTask.java b/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveTask.java index d49816692..04353f589 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveTask.java @@ -1,18 +1,14 @@ package com.csse3200.game.components.tasks.waves; -import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.audio.Sound; 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.services.GameTime; import com.csse3200.game.services.ResourceService; import com.csse3200.game.services.ServiceLocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; - public class WaveTask extends DefaultTask implements PriorityTask { private static final Logger logger = LoggerFactory.getLogger(WaveTask.class); private LevelWaves level; @@ -21,10 +17,6 @@ public class WaveTask extends DefaultTask implements PriorityTask { private long nextWaveAt = 0; private int currentWaveIndex = 0; private boolean waveInProgress; - private float startTime = 0; - private float endTime = 0; - private final float INITIAL_WAIT_INTERVAL = 10; - private final int SPAWNING_INTERVAL = 10; private static final String[] waveSounds = { "sounds/waves/wave-start/Wave_Start_Alarm.ogg", @@ -83,7 +75,6 @@ public void start() { this.currentWave = level.getWave(currentWaveIndex); ServiceLocator.getWaveService().setEnemyCount(currentWave.getSize()); logger.info("Wave {} starting with {} enemies", currentWaveIndex, ServiceLocator.getWaveService().getEnemyCount()); - // endTime = globalTime.getTime() + (SPAWNING_INTERVAL * 1000); } /** @@ -101,7 +92,6 @@ public void update() { } else { // Spawn the next wave -// logger.info("No enemies remaining, begin next wave"); if (nextWaveAt == 0) { logger.info("Next wave in 10 seconds"); this.waveEnd.play(); @@ -121,16 +111,11 @@ public void update() { this.currentWave = this.level.getWave(currentWaveIndex); ServiceLocator.getWaveService().setEnemyCount(currentWave.getSize()); logger.info("Next wave {} starting with {} enemies", currentWaveIndex, ServiceLocator.getWaveService().getEnemyCount()); - //endTime = globalTime.getTime() + (SPAWNING_INTERVAL * 1000L); // reset end time } } } } else { - //logger.info("{} enemies remaining in wave {}", ServiceLocator.getWaveService().getEnemyCount(), currentWaveIndex); - //logger.info("WAVE SERVICE NUMBER: Wave Number {}",ServiceLocator.getWaveService().getWaveCount()); - //logger.info("NEXT WAVE AT {}", ServiceLocator.getWaveService().getNextWaveTime()); - //logger.info("TIME IS {}", ServiceLocator.getTimeSource().getTime()); if (waveInProgress) { this.level.spawnWave(); } From 9fb461453001f2e375bf0dff14f448f8888e1f9d Mon Sep 17 00:00:00 2001 From: max9753 Date: Mon, 16 Oct 2023 22:35:20 +1000 Subject: [PATCH 09/20] Cleaned up several sections of NPCFactory.java and left COW's testing area alone. Hopefully none of NPC factory was messed with :) --- .../game/entities/factories/NPCFactory.java | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) 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 65185b27e..e8038c978 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 @@ -64,9 +64,7 @@ public static Entity createGhost() { **/ ghost .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) - // .addComponent(animator) .addComponent(new TextureRenderComponent("images/mobs/satyr.png")); - // .addComponent(new GhostAnimationController()); ghost.getComponent(TextureRenderComponent.class).scaleEntity(); @@ -103,8 +101,6 @@ public static Entity createGhostKing() { * * @return entity */ -// public static Entity createSkeleton(int health) { -// Entity skeleton = createBaseNPC(int health); public static Entity createSkeleton(int health) { Entity skeleton = createBaseNPC(); ArrayList drops = new ArrayList<>(); @@ -396,7 +392,7 @@ public static Entity createNecromancer(int health) { } public static Entity createFirewizard(int health) { - Entity Firewizard = createBaseNPC(); + Entity fireWizard = createBaseNPC(); ArrayList drops = new ArrayList<>(); AnimationRenderComponent animator = @@ -410,16 +406,16 @@ public static Entity createFirewizard(int health) { AITaskComponent aiTaskComponent = new AITaskComponent() .addTask(new MobTask(MobType.FIREWIZARD)); - Firewizard + fireWizard .addComponent(new CombatStatsComponent(health, 0, drops)) .addComponent(animator) .addComponent(new FirewizardAnimationController()) .addComponent(aiTaskComponent); - Firewizard.getComponent(HitboxComponent.class).setAsBoxAligned(new Vector2(.3f, .5f), PhysicsComponent.AlignX.RIGHT, PhysicsComponent.AlignY.BOTTOM); - Firewizard.getComponent(AnimationRenderComponent.class).scaleEntity(); + fireWizard.getComponent(HitboxComponent.class).setAsBoxAligned(new Vector2(.3f, .5f), PhysicsComponent.AlignX.RIGHT, PhysicsComponent.AlignY.BOTTOM); + fireWizard.getComponent(AnimationRenderComponent.class).scaleEntity(); - return Firewizard; + return fireWizard; } public static Entity createArcaneArcher(int health) { @@ -503,7 +499,6 @@ public static Entity createXenoGrunt(int health) { animator.addAnimation(DEFAULT, 0.1f); xenoGrunt .addComponent(new CombatStatsComponent(health, config.baseAttack, drops, melee, projectiles)) -// .addComponent(new CombatStatsComponent(config.fullHeath, config.baseAttack, drops, melee, projectiles)) .addComponent(animator) .addComponent(new XenoAnimationController()); @@ -536,10 +531,7 @@ public static Entity createMeleeBaseNPC() { new AITaskComponent() .addTask(new MobWanderTask(2f)) .addTask(new MobMeleeAttackTask(2)); - // .addTask(new MobAttackTask(2, 2f)); - // .addTask(new MeleeMobTask(new Vector2(2f, 2f), 2f)); - // .addTask(new MobAttackTask(2, 40)); Entity npc = new Entity() .addComponent(new PhysicsComponent()) @@ -560,11 +552,8 @@ public static Entity createRangedBaseNPC() { AITaskComponent aiComponent = new AITaskComponent() .addTask(new MobWanderTask(2f)) - // .addTask(new MobAttackTask(2, 2f)); .addTask(new MobRangedAttackTask(2)); - // .addTask(new MeleeMobTask(new Vector2(2f, 2f), 2f)); - // .addTask(new MobAttackTask(2, 40)); Entity npc = new Entity() .addComponent(new PhysicsComponent()) @@ -634,9 +623,6 @@ public static Entity createDodgingDragonKnight(int health) { Entity dodgeKnight = createDragonKnight(health); dodgeKnight.addComponent(new DodgingComponent(PhysicsLayer.PROJECTILE, 0.25f)); - // dodgeKnight.getComponent(AITaskComponent.class).addTask(new MobDodgeTask(new Vector2(2f, 2f), 2f, 5)); - // dodgeKnight.getComponent(AITaskComponent.class). - // addTask(new MobDodgeTask(MobType.DRAGON_KNIGHT, 5)); dodgeKnight.getComponent(AITaskComponent.class).getTask(MobTask.class).setDodge(true); PhysicsUtils.setScaledCollider(dodgeKnight, 0.3f, 0.7f); dodgeKnight.setScale(0.3f, 0.7f); @@ -653,9 +639,6 @@ public static Entity createDodgingArcaneArcher(int health) { Entity dodgeKnight = createArcaneArcher(health); dodgeKnight.addComponent(new DodgingComponent(PhysicsLayer.PROJECTILE, 0.25f)); - // dodgeKnight.getComponent(AITaskComponent.class).addTask(new MobDodgeTask(new Vector2(2f, 2f), 2f, 5)); - // dodgeKnight.getComponent(AITaskComponent.class). - // addTask(new MobDodgeTask(MobType.DRAGON_KNIGHT, 5)); dodgeKnight.getComponent(AITaskComponent.class).getTask(MobTask.class).setDodge(true); PhysicsUtils.setScaledCollider(dodgeKnight, 0.3f, 0.7f); dodgeKnight.setScale(0.3f, 0.7f); @@ -663,9 +646,6 @@ public static Entity createDodgingArcaneArcher(int health) { return dodgeKnight; } -// public static Entity createDeflectXenoGrunt(int health) { -// Entity deflectXenoGrunt = createXenoGrunt(health); -// deflectXenoGrunt.addComponent(new DeflectingComponent( /** * Creates a wizard that can deflect bullets * @return Deflecting wizard From 12c852f206c9d1eba57d32d8e63922a6d6f658ec Mon Sep 17 00:00:00 2001 From: max9753 Date: Mon, 16 Oct 2023 22:50:52 +1000 Subject: [PATCH 10/20] Fixed several code smells in WaveFactoryTest about assertTrue should be assertEquals etc. --- .../entities/factories/WaveFactoryTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/core/src/test/com/csse3200/game/entities/factories/WaveFactoryTest.java b/source/core/src/test/com/csse3200/game/entities/factories/WaveFactoryTest.java index eca488c8f..6db312e94 100644 --- a/source/core/src/test/com/csse3200/game/entities/factories/WaveFactoryTest.java +++ b/source/core/src/test/com/csse3200/game/entities/factories/WaveFactoryTest.java @@ -155,7 +155,7 @@ void testLevel1Creation() { if (waveNum != 5) { Set mobNames = new HashSet<>(wave.getEntities().keySet()); Set expectedMobNames = new HashSet<>(LVL1_WAVES_STRUC.get(waveNum - 1)); - assertTrue(mobNames.equals(expectedMobNames), "The mobs in the wave should be " + expectedMobNames + " ."); + assertEquals(mobNames, expectedMobNames, "The mobs in the wave should be " + expectedMobNames + " ."); for (String key: wave.getEntities().keySet()) { int[] values = wave.getEntities().get(key); @@ -164,16 +164,16 @@ void testLevel1Creation() { mobsRemaining --; if (MELEE_MOBS.contains(key)) { - assertTrue(values[1] == MIN_MELEE_HEALTH + waveNum, "The health of the mob should be " + MIN_MELEE_HEALTH + waveNum + " ."); + assertEquals(values[1], MIN_MELEE_HEALTH + waveNum, "The health of the mob should be " + MIN_MELEE_HEALTH + waveNum + " ."); } else { - assertTrue(values[1] == MIN_RANGE_HEALTH + waveNum, "The health of the mob should be " + MIN_RANGE_HEALTH + waveNum + " ."); + assertEquals(values[1], MIN_RANGE_HEALTH + waveNum, "The health of the mob should be " + MIN_RANGE_HEALTH + waveNum + " ."); } } } else { - assertTrue(wave.getEntities().keySet().size() == 1); + assertEquals(wave.getEntities().keySet().size(), 1); for (String key: wave.getEntities().keySet()) { int[] values = wave.getEntities().get(key); - assertTrue(values[1] == bossHealth, "The health of the boss should be " + MIN_BOSS_HEALTH); + assertEquals(values[1], bossHealth, "The health of the boss should be " + MIN_BOSS_HEALTH); } } mobCount ++; @@ -207,7 +207,7 @@ void testLevel2Creation() { if (waveNum != 10) { Set mobNames = new HashSet<>(wave.getEntities().keySet()); Set expectedMobNames = new HashSet<>(LVL2_WAVES_STRUC.get(waveNum - 1)); - assertTrue(mobNames.equals(expectedMobNames), "The mobs in the wave should be " + expectedMobNames + " ."); + assertEquals(mobNames, expectedMobNames, "The mobs in the wave should be " + expectedMobNames + " ."); for (String key: wave.getEntities().keySet()) { int[] values = wave.getEntities().get(key); @@ -231,7 +231,7 @@ void testLevel2Creation() { assertTrue(wave.getEntities().keySet().size() == 1); for (String key: wave.getEntities().keySet()) { int[] values = wave.getEntities().get(key); - assertTrue(values[1] == bossHealth, "The health of the boss should be " + MIN_BOSS_HEALTH); + assertEquals(values[1], bossHealth, "The health of the boss should be " + MIN_BOSS_HEALTH); } } mobCount ++; @@ -266,7 +266,7 @@ void testLevel3Creation() { if (waveNum != 15) { Set mobNames = new HashSet<>(wave.getEntities().keySet()); Set expectedMobNames = new HashSet<>(LVL3_WAVES_STRUC.get(waveNum - 1)); - assertTrue(mobNames.equals(expectedMobNames), "The mobs in the wave should be " + expectedMobNames + " ."); + assertEquals(mobNames, expectedMobNames, "The mobs in the wave should be " + expectedMobNames + " ."); for (String key: wave.getEntities().keySet()) { int[] values = wave.getEntities().get(key); @@ -283,10 +283,10 @@ void testLevel3Creation() { } } else { - assertTrue(wave.getEntities().keySet().size() == 1); + assertEquals(wave.getEntities().keySet().size(), 1); for (String key: wave.getEntities().keySet()) { int[] values = wave.getEntities().get(key); - assertTrue(values[1] == bossHealth, "The health of the boss should be " + MIN_BOSS_HEALTH); + assertEquals(values[1], bossHealth, "The health of the boss should be " + MIN_BOSS_HEALTH); } } mobCount ++; From 289adffe5b883b283b4ce3c3871ba72c63b9aa10 Mon Sep 17 00:00:00 2001 From: max9753 Date: Mon, 16 Oct 2023 23:06:54 +1000 Subject: [PATCH 11/20] Rolled back some stuff because I merged poorly --- .../game/components/npc/SplitMoblings.java | 3 ++- .../game/entities/factories/NPCFactory.java | 25 +++++++++++++++++++ .../game/entities/factories/WaveFactory.java | 5 ---- .../game/components/SplitMoblingsTest.java | 2 +- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java b/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java index 3fd20bb9d..1127cd4ad 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java +++ b/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java @@ -167,7 +167,8 @@ public void spawnAdditionalMob(float positionX, float positionY, ServiceLocator.getEntityService().register(entityType); - ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); + // ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); + //ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); } /** 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 224bb8f8b..e8038c978 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 @@ -71,6 +71,31 @@ public static Entity createGhost() { return ghost; } + /** + * Creates a ghost king entity. + * + * @return entity + */ + public static Entity createGhostKing() { + Entity ghostKing = createMeleeBaseNPC(); + GhostKingConfig config = configs.ghostKing; + + AnimationRenderComponent animator = + new AnimationRenderComponent( + ServiceLocator.getResourceService() + .getAsset("images/ghostKing.atlas", TextureAtlas.class)); + animator.addAnimation("float", 0.2f, Animation.PlayMode.LOOP); + animator.addAnimation("angry_float", 0.2f, Animation.PlayMode.LOOP); + + ghostKing + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + .addComponent(animator) + .addComponent(new GhostAnimationController()); + + ghostKing.getComponent(AnimationRenderComponent.class).scaleEntity(); + return ghostKing; + } + /** * Creates a fire worm entity. * diff --git a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java index 76dbecc94..53ea2007a 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java @@ -67,11 +67,6 @@ public class WaveFactory { )), new ArrayList<>(Arrays.asList("FireWorm", "SplittingRocky", "Necromancer" )), new ArrayList<>(Arrays.asList("SplittingRocky", "DeflectFireWizard", "FireWorm" )), new ArrayList<>(Arrays.asList("DeflectFireWizard", "SplittingRocky", "Necromancer", "DodgingDragon", "FireWorm" - )), new ArrayList<>(Arrays.asList("DeflectFireWizard","SplittingRocky", "Necromancer" - )), new ArrayList<>(Arrays.asList("DodgingDragon", "DeflectFireWizard", "SplittingRocky", "Necromancer" - )), new ArrayList<>(Arrays.asList("FireWorm", "DeflectWizard", "DodgingDragon" - )), new ArrayList<>(Arrays.asList("FireWorm", "DeflectWizard", "Necromancer" - )), new ArrayList<>(Arrays.asList("Necromancer", "DeflectFireWizard", "SplittingRocky", "DodgingDragon", "FireWorm" )) )); diff --git a/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java b/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java index 49fdeeee0..d21af300c 100644 --- a/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java +++ b/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java @@ -66,7 +66,7 @@ public void setUp() { resourceService.loadTextureAtlases(atlas); resourceService.loadAll(); - WaveService waveService = mock(WaveService.class); + WaveService waveService = new WaveService(); ServiceLocator.registerWaveService(waveService); baseMob = createSplitMob(BASE_AMOUNT); From 006bfd64e45309c934f8bdda65ee92776891bfa7 Mon Sep 17 00:00:00 2001 From: bojyyy <140468434+bojyyy@users.noreply.github.com> Date: Mon, 16 Oct 2023 23:10:30 +1000 Subject: [PATCH 12/20] Fixing merge conflicts --- .../main/com/csse3200/game/components/npc/SplitMoblings.java | 3 +-- .../test/com/csse3200/game/components/SplitMoblingsTest.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java b/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java index 1127cd4ad..3fd20bb9d 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java +++ b/source/core/src/main/com/csse3200/game/components/npc/SplitMoblings.java @@ -167,8 +167,7 @@ public void spawnAdditionalMob(float positionX, float positionY, ServiceLocator.getEntityService().register(entityType); - // ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); - //ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); + ServiceLocator.getWaveService().setEnemyCount(ServiceLocator.getWaveService().getEnemyCount() + 1); } /** diff --git a/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java b/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java index d21af300c..49fdeeee0 100644 --- a/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java +++ b/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java @@ -66,7 +66,7 @@ public void setUp() { resourceService.loadTextureAtlases(atlas); resourceService.loadAll(); - WaveService waveService = new WaveService(); + WaveService waveService = mock(WaveService.class); ServiceLocator.registerWaveService(waveService); baseMob = createSplitMob(BASE_AMOUNT); From 4ab9b82ea152ee734585a1c7586fbb49ce422388 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Mon, 16 Oct 2023 23:41:31 +1000 Subject: [PATCH 13/20] Removing code smells from forest game area --- source/core/assets/images/mobs/skeleton.atlas | 49 +-- source/core/assets/images/mobs/skeleton.png | Bin 26501 -> 26617 bytes .../csse3200/game/areas/ForestGameArea.java | 402 +----------------- .../game/entities/factories/NPCFactory.java | 2 +- .../game/entities/factories/WaveFactory.java | 4 +- 5 files changed, 11 insertions(+), 446 deletions(-) diff --git a/source/core/assets/images/mobs/skeleton.atlas b/source/core/assets/images/mobs/skeleton.atlas index f0117d879..9e1c78e17 100644 --- a/source/core/assets/images/mobs/skeleton.atlas +++ b/source/core/assets/images/mobs/skeleton.atlas @@ -69,7 +69,7 @@ skeleton_attack index: -1 skeleton_attack rotate: false - xy: 514, 2 + xy: 578, 52 size: 62, 48 orig: 62, 48 offset: 0, 0 @@ -83,7 +83,7 @@ skeleton_attack index: -1 skeleton_attack rotate: false - xy: 770, 2 + xy: 834, 52 size: 62, 48 orig: 62, 48 offset: 0, 0 @@ -95,41 +95,6 @@ skeleton_attack orig: 62, 48 offset: 0, 0 index: -1 -skeleton_death - rotate: false - xy: 1026, 2 - size: 62, 48 - orig: 62, 48 - offset: 0, 0 - index: -1 -skeleton_death - rotate: false - xy: 66, 52 - size: 62, 48 - orig: 62, 48 - offset: 0, 0 - index: -1 -skeleton_death - rotate: false - xy: 834, 52 - size: 62, 48 - orig: 62, 48 - offset: 0, 0 - index: -1 -skeleton_death - rotate: false - xy: 1154, 55 - size: 62, 45 - orig: 62, 45 - offset: 0, 0 - index: -1 -skeleton_death - rotate: false - xy: 578, 2 - size: 62, 48 - orig: 62, 48 - offset: 0, 0 - index: -1 skeleton_death rotate: false xy: 194, 52 @@ -188,7 +153,7 @@ skeleton_walk index: -1 skeleton_walk rotate: false - xy: 514, 52 + xy: 514, 2 size: 62, 48 orig: 62, 48 offset: 0, 0 @@ -202,7 +167,7 @@ skeleton_walk index: -1 skeleton_walk rotate: false - xy: 770, 52 + xy: 770, 2 size: 62, 48 orig: 62, 48 offset: 0, 0 @@ -237,21 +202,21 @@ skeleton_walk index: -1 skeleton_walk rotate: false - xy: 578, 52 + xy: 514, 52 size: 62, 48 orig: 62, 48 offset: 0, 0 index: -1 skeleton_walk rotate: false - xy: 130, 2 + xy: 66, 52 size: 62, 48 orig: 62, 48 offset: 0, 0 index: -1 skeleton_walk rotate: false - xy: 834, 2 + xy: 770, 52 size: 62, 48 orig: 62, 48 offset: 0, 0 diff --git a/source/core/assets/images/mobs/skeleton.png b/source/core/assets/images/mobs/skeleton.png index cbcbd1ed123c4a385f11fd030d7bfe924ac773e2..9d75bb0669074f5bf188193778fcdae0b5296159 100644 GIT binary patch literal 26617 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 literal 26501 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 Ntu5@#OHX*m{6E*b7EAyD 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 8fccaf74b..1d5327884 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -263,30 +263,9 @@ public void create() { waves = WaveFactory.createWaves(); spawnEntity(waves); waves.getEvents().addListener("spawnWave", this::spawnMob); - // spawnCoat(); -// spawnDodgingDragonKnight(17,4); -// spawnDeflectWizard(17, 3); -// spawnSplittingXenoGrunt(17, 2); -// spawnPatrick(); -// spawnDemonBoss(); - // spawnSplittingRocky(17, 4); - // spawnFireWizard(17, 3); - // spawnNecromancer(17, 2); - - - spawnScrap(); spawnGapScanners(); - -// spawnTNTTower(); -// spawnWeaponTower(); -// spawnGapScanners(); -// spawnDroidTower(); -// spawnFireWorksTower(); // Commented these out until they are needed for Demonstration -// spawnPierceTower(); -// spawnRicochetTower(); -// spawnBombship(); } private void displayUI() { @@ -344,90 +323,6 @@ private Entity spawnPlayer(GridPoint2 position) { return newPlayer; } - // commented 383 - 386 out as there was a missing arg? - private void spawnDemonBoss() { - Entity demon = MobBossFactory.createDemonBoss(5000); - spawnEntityAt(demon, new GridPoint2(19, 5), true, false); - } - - private void spawnPatrick() { - Entity patrick = MobBossFactory.createPatrickBoss(3000); - spawnEntityAt(patrick, new GridPoint2(18, 5), true, false); - } - - private void spawnPatrickDeath() { - Entity patrickDeath = MobBossFactory.patrickDead(); - spawnEntityAt(patrickDeath, new GridPoint2(18, 5), true, false); - } - // commented 398 - 401 out as there was a missing arg? -// private void spawnIceBaby() { -// Entity iceBaby = MobBossFactory.createIceBoss(); -// spawnEntityAt(iceBaby, new GridPoint2(19, 5), true, false); -// } - -// private void spawnDemonBoss() { -// Entity demon = MobBossFactory.createDemonBoss(); -// spawnEntityAt(demon, new GridPoint2(19, 5), true, false); -// } - -// private void spawnPatrick() { -// Entity patrick = MobBossFactory.createPatrickBoss(3000); -// spawnEntityAt(patrick, new GridPoint2(18, 5), true, false); -// } -// -// private void spawnPatrickDeath() { -// Entity patrickDeath = MobBossFactory.patrickDead(); -// spawnEntityAt(patrickDeath, new GridPoint2(18, 5), true, false); -// } -// -// private void spawnIceBaby() { -// Entity iceBaby = MobBossFactory.createIceBoss(); -// spawnEntityAt(iceBaby, new GridPoint2(19, 5), true, false); -// } - - /** - * Spawns a projectile that only heads towards the enemies in its lane. - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - */ - private void spawnProjectile(Vector2 position, short targetLayer, int direction, Vector2 speed) { - Entity Projectile = ProjectileFactory.createFireBall(targetLayer, new Vector2(direction, position.y), speed); - Projectile.setPosition(position); - spawnEntity(Projectile); - } - - /** - * Spawns a projectile specifically for general mobs/xenohunters - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - */ - private void spawnProjectileTest(Vector2 position, short targetLayer, int direction, Vector2 speed) { - Entity Projectile = ProjectileFactory.createEngineerBullet(targetLayer, new Vector2(direction, position.y), speed); - Projectile.setPosition(position); - spawnEntity(Projectile); - } - - /** - * Spawns a projectile to be used for multiple projectile function. - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param space The space between the projectiles' destination. - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - */ - private void spawnProjectile(Vector2 position, short targetLayer, int space, int direction, Vector2 speed) { - Entity Projectile = ProjectileFactory.createFireBall(targetLayer, new Vector2(direction, position.y + space), speed); - Projectile.setPosition(position); - spawnEntity(Projectile); - } - /** * Spawn an entity on the map. Is called during a wave. Add cases here for each mob type * @param entity mob to be spawned @@ -449,7 +344,7 @@ public void spawnMob(String entity, GridPoint2 randomPos, int health) { case "Skeleton": mob = NPCFactory.createSkeleton(health); break; - case "DeflectWizard": + case "Wizard": mob = NPCFactory.createWizard(health); break; case "WaterQueen": @@ -499,301 +394,6 @@ public void spawnMob(String entity, GridPoint2 randomPos, int health) { spawnEntityAt(mob, randomPos, true, false); } - // * TEMPORARY FOR TESTING -// private void spawnSplittingXenoGrunt(int x, int y) { -// GridPoint2 pos = new GridPoint2(x, y); -// Entity xenoGrunt = NPCFactory.createSplittingXenoGrunt(); -// xenoGrunt.setScale(1.5f, 1.5f); -// spawnEntityAt(xenoGrunt, pos, true, true); -// } - // * TEMPORARY FOR TESTING - private void spawnSplittingRocky(int x, int y) { - GridPoint2 pos = new GridPoint2(x, y); - Entity rocky = NPCFactory.createSplittingRocky(60); - rocky.setScale(1.5f, 1.5f); - spawnEntityAt(rocky, pos, true, true); - } - - private void spawnFireWizard(int x, int y) { - GridPoint2 pos = new GridPoint2(x, y); - Entity firewiz = NPCFactory.createDeflectFireWizard(60); - firewiz.setScale(1.5f, 1.5f); - spawnEntityAt(firewiz, pos, true, true); - } - - private void spawnNecromancer(int x, int y) { - GridPoint2 pos = new GridPoint2(x, y); - Entity Necromancer = NPCFactory.createNecromancer(60); - Necromancer.setScale(1.5f, 1.5f); - spawnEntityAt(Necromancer, pos, true, true); - } - - // * TEMPORARY FOR TESTING -// private void spawnDodgingDragonKnight(int x, int y) { -// GridPoint2 pos = new GridPoint2(x, y); -// Entity fireworm = NPCFactory.createDodgingDragonKnight(); -// fireworm.setScale(1.5f, 1.5f); -// spawnEntityAt(fireworm, pos, true, true); -// } -// -// // * TEMPORARY FOR TESTING -// private void spawnDeflectXenoGrunt(int x, int y) { -// GridPoint2 pos = new GridPoint2(x, y); -// Entity xenoGrunt = NPCFactory.createDeflectXenoGrunt(); -// xenoGrunt.setScale(1.5f, 1.5f); -// spawnEntityAt(xenoGrunt, pos, true, true); -// } -// -// private void spawnFireWorm() { -// int[] pickedLanes = random.ints(1, 7) -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity fireWorm = NPCFactory.createFireWorm(); -// fireWorm.setScale(1.5f, 1.5f); -// spawnEntityAt(fireWorm, randomPos, true, false); -// } -// } -// -// private void spawnSkeleton() { -// int[] pickedLanes = new Random().ints(1, 7) -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity skeleton = NPCFactory.createSkeleton(); -// skeleton.setScale(1.5f, 1.5f); -// spawnEntityAt(skeleton, randomPos, true, false); -// } -// } - -// private void spawnDragonKnight() { -// int[] pickedLanes = random.ints(1, 7) -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity fireWorm = NPCFactory.createDragonKnight(); -// fireWorm.setScale(1.5f, 1.5f); -// spawnEntityAt(fireWorm, randomPos, true, false); -// } -// } -// -// private void spawnWizard() { -// int[] pickedLanes = new Random().ints(1, 7) -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity wizard = NPCFactory.createWizard(); -// wizard.setScale(1.5f, 1.5f); -// spawnEntityAt(wizard, randomPos, true, false); -// } -// } -// -// private void spawnWaterQueen() { -// int[] pickedLanes = new Random().ints(1, 7) -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity waterQueen = NPCFactory.createWaterQueen(); -// waterQueen.setScale(1.5f, 1.5f); -// spawnEntityAt(waterQueen, randomPos, true, false); -// } -// } -// -// private void spawnWaterSlime() { -// int[] pickedLanes = new Random().ints(1, 7) -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity waterSlime = NPCFactory.createWaterSlime(); -// waterSlime.setScale(1.5f, 1.5f); -// spawnEntityAt(waterSlime, randomPos, true, false); -// } -// } - - // * TEMPORARY FOR TESTING - private void spawnDodgingDragonKnight(int x, int y) { - GridPoint2 pos = new GridPoint2(x, y); - Entity fireworm = NPCFactory.createDodgingDragonKnight(100); - fireworm.setScale(1.5f, 1.5f); - spawnEntityAt(fireworm, pos, true, true); - } -// -// // * TEMPORARY FOR TESTING - private void spawnDeflectWizard(int x, int y) { - GridPoint2 pos = new GridPoint2(x, y); - Entity xenoGrunt = NPCFactory.createDeflectWizard(100); - xenoGrunt.setScale(1.5f, 1.5f); - spawnEntityAt(xenoGrunt, pos, true, true); - } -// -// private void spawnFireWorm() { -// -// int[] pickedLanes = rand.ints(0, ServiceLocator.getMapService().getHeight() ) -// -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity fireWorm = NPCFactory.createFireWorm(); -// fireWorm.setScale(1.5f, 1.5f); -// spawnEntityAt(fireWorm, randomPos, true, false); -// } -// } -// -// private void spawnSkeleton() { -// -// int[] pickedLanes = new Random().ints(0, ServiceLocator.getMapService().getHeight() ) -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity skeleton = NPCFactory.createSkeleton(); -// skeleton.setScale(1.5f, 1.5f); -// spawnEntityAt(skeleton, randomPos, true, false); -// } -// } -// -// private void spawnDragonKnight() { -// -// int[] pickedLanes = rand.ints(0, ServiceLocator.getMapService().getHeight() ) -// -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity fireWorm = NPCFactory.createDodgingDragonKnight(); -// fireWorm.setScale(1.5f, 1.5f); -// spawnEntityAt(fireWorm, randomPos, true, false); -// } -// } -// -// private void spawnWizard() { -// -// int[] pickedLanes = rand.ints(0, ServiceLocator.getMapService().getHeight() ) -// -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity wizard = NPCFactory.createDeflectWizard(); -// wizard.setScale(1.5f, 1.5f); -// spawnEntityAt(wizard, randomPos, true, false); -// } -// } -// -// private void spawnWaterQueen() { -// -// int[] pickedLanes = new Random().ints(0, ServiceLocator.getMapService().getHeight() ) -// -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity waterQueen = NPCFactory.createWaterQueen(); -// waterQueen.setScale(1.5f, 1.5f); -// spawnEntityAt(waterQueen, randomPos, true, false); -// } -// } -// -// private void spawnWaterSlime() { -// -// int[] pickedLanes = new Random().ints(0, ServiceLocator.getMapService().getHeight() ) -// -// .distinct().limit(5).toArray(); -// for (int i = 0; i < NUM_GRUNTS; i++) { -// GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); -// Entity waterSlime = NPCFactory.createSplittingWaterSlime(); -// waterSlime.setScale(1.5f, 1.5f); -// spawnEntityAt(waterSlime, randomPos, true, false); -// } -// } - - private void spawnCoat() { - Entity gregMob = NPCFactory.createCoat(100); - gregMob.setScale(1.5f, 1.5f); - spawnEntityAt(gregMob, new GridPoint2(17, 4), false, false); - } - - /** - * Creates multiple projectiles that travel simultaneous. They all have same - * the starting point but different destinations. - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param space The space between the projectiles' destination. - * @param speed The speed of the projectiles. - * @param quantity The amount of projectiles to spawn. - */ - private void spawnMultiProjectile(Vector2 position, short targetLayer, int direction, int space, Vector2 speed, int quantity) { - int half = quantity / 2; - for (int i = 0; i < quantity; i++) { - spawnProjectile(position, targetLayer, space * half, direction, speed); - --half; - } - } - - /** - * Returns projectile that can do an area of effect damage - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - * @param effect Type of effect. - * @param aoe Whether it is an aoe projectile. - */ - private void spawnEffectProjectile(Vector2 position, short targetLayer, int direction, Vector2 speed, - ProjectileEffects effect, boolean aoe) { - Entity Projectile = ProjectileFactory.createEffectProjectile(targetLayer, new Vector2(direction, position.y), speed, effect, aoe); - Projectile.setPosition(position); - spawnEntity(Projectile); - } - - /** - * Spawns a pierce fireball. - * Pierce fireball can go through targetlayers without disappearing but damage - * will still be applied. - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - */ - private void spawnPierceFireBall(Vector2 position, short targetLayer, int direction, Vector2 speed) { - Entity projectile = ProjectileFactory.createPierceFireBall(targetLayer, new Vector2(direction, position.y), speed); - projectile.setPosition(position); - spawnEntity(projectile); - } - - /** - * Spawns a ricochet fireball - * Ricochet fireballs bounce off targets with a specified maximum count of 3 - * Possible extensions: Make the bounce count flexible with a param. - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should head towards. - * @param speed The speed of the projectiles. - */ - private void spawnRicochetFireball(Vector2 position, short targetLayer, int direction, Vector2 speed) { - // Bounce count set to 0. - Entity projectile = ProjectileFactory.createRicochetFireball(targetLayer, new Vector2(direction, position.y), speed, 0); - projectile.setPosition(position); - spawnEntity(projectile); - } - - /** - * Spawns a split firework fireball. - * Splits into mini projectiles that spreads out after collision. - * - * @param position The position of the Entity that's shooting the projectile. - * @param targetLayer The enemy layer of the "shooter". - * @param direction The direction the projectile should towards. - * @param speed The speed of the projectiles. - * @param amount The amount of projectiles appearing after collision. - */ - private void spawnSplitFireWorksFireBall(Vector2 position, short targetLayer, int direction, Vector2 speed, int amount) { - Entity projectile = ProjectileFactory.createSplitFireWorksFireball(targetLayer, new Vector2(direction, position.y), speed, amount); - projectile.setPosition(position); - spawnEntity(projectile); - } - private void spawnWeaponTower() { GridPoint2 minPos = new GridPoint2(0, 0); GridPoint2 maxPos = terrain.getMapBounds(0).sub(5, 1); 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 513f92eeb..943023e48 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 @@ -622,7 +622,7 @@ public static Entity createSplittingRocky(int health) { * @return Splitting Night Borne */ public static Entity createSplittingNightBorne(int health) { - return createNightBorne(health).addComponent(new SplitMoblings(MobType.NIGHT_BORNE, 7, 0.5f)); + return createNightBorne(health).addComponent(new SplitMoblings(MobType.NIGHT_BORNE, 7, 0.3f)); } /** diff --git a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java index de95970f8..955da4cc4 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java @@ -41,10 +41,10 @@ public class WaveFactory { private static final ArrayList> lvl2Structure = new ArrayList<>(Arrays.asList( new ArrayList<>(Arrays.asList("Skeleton" - )), new ArrayList<>(Arrays.asList("Skeleton", "ArcaneArcher" + )), new ArrayList<>(Arrays.asList("Skeleton", "Wizard" )), new ArrayList<>(Arrays.asList("Skeleton", "Wizard" )), new ArrayList<>(Arrays.asList("Skeleton", "SplittingNightBorne" - )), new ArrayList<>(Arrays.asList("Wizard", "SplittingNightBorne" + )), new ArrayList<>(Arrays.asList("ArcaneArcher", "SplittingNightBorne" )), new ArrayList<>(Arrays.asList("SplittingNightBorne", "Skeleton" )), new ArrayList<>(Arrays.asList("Wizard", "SplittingNightBorne" )), new ArrayList<>(Arrays.asList("ArcaneArcher", "SplittingNightBorne", "Wizard" From 80915ea38c785e7312d9dee1177b0120329b6f52 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Mon, 16 Oct 2023 23:47:30 +1000 Subject: [PATCH 14/20] Fixing waves structure for Junit testing --- .../com/csse3200/game/entities/factories/WaveFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java index 955da4cc4..de95970f8 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java @@ -41,10 +41,10 @@ public class WaveFactory { private static final ArrayList> lvl2Structure = new ArrayList<>(Arrays.asList( new ArrayList<>(Arrays.asList("Skeleton" - )), new ArrayList<>(Arrays.asList("Skeleton", "Wizard" + )), new ArrayList<>(Arrays.asList("Skeleton", "ArcaneArcher" )), new ArrayList<>(Arrays.asList("Skeleton", "Wizard" )), new ArrayList<>(Arrays.asList("Skeleton", "SplittingNightBorne" - )), new ArrayList<>(Arrays.asList("ArcaneArcher", "SplittingNightBorne" + )), new ArrayList<>(Arrays.asList("Wizard", "SplittingNightBorne" )), new ArrayList<>(Arrays.asList("SplittingNightBorne", "Skeleton" )), new ArrayList<>(Arrays.asList("Wizard", "SplittingNightBorne" )), new ArrayList<>(Arrays.asList("ArcaneArcher", "SplittingNightBorne", "Wizard" From e0701d9ad8e0e4226eba1fde0101eb09edada328 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Mon, 16 Oct 2023 23:54:48 +1000 Subject: [PATCH 15/20] Fixing up night borne splitting --- .../main/com/csse3200/game/entities/factories/NPCFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 943023e48..513f92eeb 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 @@ -622,7 +622,7 @@ public static Entity createSplittingRocky(int health) { * @return Splitting Night Borne */ public static Entity createSplittingNightBorne(int health) { - return createNightBorne(health).addComponent(new SplitMoblings(MobType.NIGHT_BORNE, 7, 0.3f)); + return createNightBorne(health).addComponent(new SplitMoblings(MobType.NIGHT_BORNE, 7, 0.5f)); } /** From fc928693ea8f9ead66700e3b09e15d5c5ad2c1d6 Mon Sep 17 00:00:00 2001 From: Kevin <104761532+Hasakev@users.noreply.github.com> Date: Tue, 17 Oct 2023 02:35:19 +1000 Subject: [PATCH 16/20] Fixed test failure caused by NullPointerException present when EngineerCountDisplay is updated --- .../components/gamearea/EngineerCountDisplay.java | 14 ++++++++------ .../game/components/tasks/MobTask/MobTask.java | 3 ++- .../game/components/SplitMoblingsTest.java | 8 ++++---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/gamearea/EngineerCountDisplay.java b/source/core/src/main/com/csse3200/game/components/gamearea/EngineerCountDisplay.java index 6334e1b4a..40c0f3b04 100644 --- a/source/core/src/main/com/csse3200/game/components/gamearea/EngineerCountDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/gamearea/EngineerCountDisplay.java @@ -64,13 +64,15 @@ private void addActors() { * Updates the engineer count on the UI component */ public void updateCount() { - int currentCount = ServiceLocator.getGameEndService().getEngineerCount(); - String text = String.format("%d", currentCount); - engineerTb.getLabel().setText(text); - if (currentCount < ServiceLocator.getGameEndService().getThreshold()) { + if (engineerTb != null) { // fix for null pointer exception + int currentCount = ServiceLocator.getGameEndService().getEngineerCount(); + String text = String.format("%d", currentCount); + engineerTb.getLabel().setText(text); + if (currentCount < ServiceLocator.getGameEndService().getThreshold()) { // engineerTb.addAction(Actions.color(Color.RED, 0.5f, Interpolation.swingIn)); - engineerTb.addAction(Actions.forever(new SequenceAction(Actions.fadeOut(0.5f), - Actions.fadeIn(0.5f)))); + engineerTb.addAction(Actions.forever(new SequenceAction(Actions.fadeOut(0.5f), + Actions.fadeIn(0.5f)))); + } } } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java b/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java index 332188cdc..2cc103bd8 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java @@ -115,13 +115,14 @@ public void update() { if(mob.getCenterPosition().x <= 1) { mob.getComponent(CombatStatsComponent.class).setHealth(0); + ServiceLocator.getGameEndService().updateEngineerCount(); } // death check if ((mob.getComponent(CombatStatsComponent.class).getHealth() <= 0 && !deathFlag)) { // decrement engineer count // ! tests failing because of textbox - // ServiceLocator.getGameEndService().updateEngineerCount(); + changeState(State.DEATH); animate(); movementTask.stop(); diff --git a/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java b/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java index 265f1730b..04295ffe3 100644 --- a/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java +++ b/source/core/src/test/com/csse3200/game/components/SplitMoblingsTest.java @@ -13,6 +13,7 @@ import java.util.ArrayList; import java.util.Arrays; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; @@ -57,6 +58,7 @@ public void setUp() { ServiceLocator.registerPhysicsService(new PhysicsService()); ServiceLocator.registerEntityService(new EntityService()); + ServiceLocator.registerGameEndService(new GameEndService()); RenderService render = new RenderService(); render.setDebug(mock(DebugRenderer.class)); @@ -79,8 +81,7 @@ public void setUp() { @Test void shouldNotBeNull() { Entity mob = createSplitMob(5); - assertNotNull("Mobling components does not exists", - mob.getComponent(SplitMoblings.class)); + Assertions.assertNotNull(mob.getComponent(SplitMoblings.class), "Mobling components does not exists"); } @Test @@ -94,8 +95,7 @@ void shouldHaveAsset() { if (entity.equals(baseMob) || entity.equals(projectile)) continue; - assertTrue("moblings does not contain the right asset", - ServiceLocator.getResourceService().containsAsset(atlas[0], entity.getClass())); + Assertions.assertTrue(ServiceLocator.getResourceService().containsAsset(atlas[0], entity.getClass()), "moblings does not contain the right asset"); } } From d206e3e837376c4893c393862ee89d5e80eeefab Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Tue, 17 Oct 2023 09:31:36 +1000 Subject: [PATCH 17/20] Cleaning up forest game area and effects component --- .../csse3200/game/areas/ForestGameArea.java | 111 -------------- .../game/components/EffectsComponent.java | 136 +----------------- 2 files changed, 1 insertion(+), 246 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 1d5327884..789f76d45 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -394,106 +394,6 @@ public void spawnMob(String entity, GridPoint2 randomPos, int health) { spawnEntityAt(mob, randomPos, true, false); } - private void spawnWeaponTower() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(5, 1); - - for (int i = 0; i < NUM_WEAPON_TOWERS + 7 ; i++) { - GridPoint2 randomPos1 = RandomUtils.random(minPos, maxPos); - GridPoint2 randomPos2 = RandomUtils.random(minPos, maxPos); - Entity wallTower = TowerFactory.createWallTower(); - Entity fireTower = TowerFactory.createFireTower(); - Entity stunTower = TowerFactory.createStunTower(); - spawnEntityAt(fireTower, randomPos1, true, true); - spawnEntityAt(stunTower, randomPos2, true, true); - spawnEntityAt(wallTower, randomPos2, true, true); - } - } - - // * TEMPORARY FOR TESTING - private void spawnFireTowerAt(int x, int y) { - GridPoint2 pos = new GridPoint2(x, y); - Entity fireTower = TowerFactory.createFireTower(); - - spawnEntityAt(fireTower, pos, true, true); - } - private void spawnDroidTowerAt(int x, int y) { - GridPoint2 pos = new GridPoint2(x, y); - Entity droidTower = TowerFactory.createDroidTower(); - - spawnEntityAt(droidTower, pos, true, true); - } - - private void spawnTNTTower() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(5, 1); - - for (int i = 0; i < NUM_WEAPON_TOWERS + 5; i++) { - GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity weaponTower = TowerFactory.createTNTTower(); - spawnEntityAt(weaponTower, randomPos, true, true); - } - - } - private void spawnFireWorksTower() { - GridPoint2 minPos = new GridPoint2(0, 2); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(1, 1); - - for (int i = 0; i < NUM_WEAPON_TOWERS; i++) { - GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity FireWorksTower = TowerFactory.createFireworksTower(); - spawnEntityAt(FireWorksTower, randomPos, true, true); - } - - } - private void spawnPierceTower() { - GridPoint2 minPos = new GridPoint2(0, 2); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(3, 3); - - for (int i = 0; i < NUM_WEAPON_TOWERS; i++) { - GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity PierceTower = TowerFactory.createPierceTower(); - spawnEntityAt(PierceTower, randomPos, true, true); - } - - } - private void spawnRicochetTower() { - GridPoint2 minPos = new GridPoint2(0, 2); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(0, 3); - - for (int i = 0; i < NUM_WEAPON_TOWERS; i++) { - GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity RicochetTower = TowerFactory.createRicochetTower(); - spawnEntityAt(RicochetTower, randomPos, true, true); - } - - } - - private void spawnBombship() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(5, 1); - Entity bombship = BombshipFactory.createBombship(); - spawnEntityAt(bombship, minPos, true, true); - } - - private void spawnDroidTower() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(5, 1); - - for (int i = 0; i < NUM_WEAPON_TOWERS + 5; i++) { - GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity weaponTower = TowerFactory.createDroidTower(); - spawnEntityAt(weaponTower, randomPos, true, false); - } - } - - private void playMusic() { - Music music = ServiceLocator.getResourceService().getAsset(BACKGROUND_MUSIC, Music.class); - music.setLooping(true); - music.setVolume(0.3f); - music.play(); - } - private void loadAssets() { logger.debug("Loading assets"); ResourceService resourceService = ServiceLocator.getResourceService(); @@ -541,17 +441,6 @@ private void spawnScrap() { } } - private void spawnIncome() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); - - for (int i = 0; i < 50; i++) { - GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity towerfactory = TowerFactory.createIncomeTower(); - spawnEntityAt(towerfactory, randomPos, true, true); - } - } - /** * Creates the scanners (one per lane) that detect absence of towers and presence of mobs, * and trigger engineer spawning diff --git a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java index 596bb71f2..4fb249d6d 100644 --- a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java @@ -2,20 +2,13 @@ import java.util.ArrayList; -import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.Fixture; -import com.csse3200.game.ai.tasks.AITaskComponent; -import com.csse3200.game.components.tower.TowerUpgraderComponent; import com.csse3200.game.entities.Entity; import com.csse3200.game.physics.BodyUserData; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.components.HitboxComponent; -import com.csse3200.game.physics.components.PhysicsMovementComponent; import com.csse3200.game.services.ServiceLocator; -import com.badlogic.gdx.utils.Timer; -import com.badlogic.gdx.utils.Timer.Task; - import com.badlogic.gdx.utils.Array; /** @@ -102,6 +95,7 @@ public void applySingleEffect(ProjectileEffects effect, Entity targetEntity) { } effectComponent.applyEffect(effect, hostEntity, targetEntity); } + /** * Used for aoe projectiles to apply effects to all entities within the area of effect (radius). * @param effect effect to be applied to entities within radius @@ -136,132 +130,4 @@ public void applyAoeEffect(ProjectileEffects effect) { effectComponent.applyEffect(effect, hostEntity, targetEntity); } } - - /** - * Deals damage to target based on hosts' CombatStatsComponent - * @param target CombatStatsComponent of entity hit by projectile - * @param host CombatStatsComponent of projectile - */ - private void fireballEffect(CombatStatsComponent target, CombatStatsComponent host) { - target.hit(host); - } - -// /** -// * Applies 5 ticks of damage from hosts' CombatStatsComponent over 5 seconds -// * @param target CombatStatsComponent of entity hit by projectile -// * @param host CombatStatsComponent of projectile -// */ -// private void burnEffect(CombatStatsComponent target, CombatStatsComponent host) { -// // Ensure burn effects aren't applied multiple times by same projectile -// if (burnEntities.contains(target, false)) { -// return; -// } -// burnEntities.add(target); -// // Create a timer task to apply the effect repeatedly -// int numberOfTicks = 5; -// long delay = 1; -// Timer.schedule(new Timer.Task() { -// private int count = 0; -// -// @Override -// public void run() { -// if (count < numberOfTicks) { -// target.hit(host); -// count++; -// } else { -// // Ensure to cancel the task when it's done -// this.cancel(); -// } -// } -// }, delay, delay); -// } - - /** - * Applies slow effect to targetEntity. If entity is a mob, speed - * and firing rate will be slowed. If entity is a tower, firing rate - * will be slowed - * @param targetEntity Entity for slow effect to be applied to - */ - private void slowEffect(Entity targetEntity) { - boolean towerFlag = false; - boolean mobFlag = false; - - PhysicsMovementComponent targetPhysics = null; - float xSpeed = 0; - float ySpeed = 0; - - // Create a timer task to apply the effect repeatedly - if (PhysicsLayer.contains(PhysicsLayer.HUMANS, targetEntity.getComponent(HitboxComponent.class).getLayer())) { - // towers - towerFlag = true; - targetEntity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.FIRERATE, -30); - } else if (PhysicsLayer.contains(PhysicsLayer.NPC, targetEntity.getComponent(HitboxComponent.class).getLayer())) { - // mobs - mobFlag = true; - targetPhysics = targetEntity.getComponent(PhysicsMovementComponent.class); - if (targetPhysics == null) { - return; - } - - // Halve the mob speed - xSpeed = targetPhysics.getSpeed().x; - ySpeed = targetPhysics.getSpeed().y; - targetPhysics.setSpeed(new Vector2(xSpeed/2, ySpeed/2)); - } else { - return; - } - - // Reset speed - boolean finalTowerFlag = towerFlag; - boolean finalMobFlag = mobFlag; - PhysicsMovementComponent finalTargetPhysics = targetPhysics; - float finalXSpeed = xSpeed; - float finalYSpeed = ySpeed; - Timer.schedule(new Task() { - @Override - public void run() { - if (finalTowerFlag) { - targetEntity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.FIRERATE, 30); - } else if (finalMobFlag) { - finalTargetPhysics.setSpeed(new Vector2(finalXSpeed, finalYSpeed)); - } - } - }, 5); // 5 seconds delay - } - - /** - * Applies stun effect to a taget entity. - * @param targetEntity Entity for stun effect to be applied to. - */ - private void stunEffect(Entity targetEntity) { - CombatStatsComponent hostCombatStats = targetEntity.getComponent(CombatStatsComponent.class); - AITaskComponent taskComponent = targetEntity.getComponent(AITaskComponent.class); - - if (hostCombatStats == null || taskComponent == null) { - return; - } - - hostCombatStats.setBaseAttack(0); - - if (stunnedEntities.contains(targetEntity)) { - return; - } - - taskComponent.disposeAll(); - stunnedEntities.add(targetEntity); - - new java.util.Timer().schedule( - new java.util.TimerTask() { - @Override - public void run() { - taskComponent.restore(); - for (int i = 0; i < stunnedEntities.size(); i++) { - if (stunnedEntities.get(i).equals(targetEntity)) { - stunnedEntities.remove(stunnedEntities.get(i)); - } - } - this.cancel(); - } - }, 5000); - } } \ No newline at end of file From 75e3b13eae980d4c6357028aaa517a0f5973c07d Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Tue, 17 Oct 2023 09:40:35 +1000 Subject: [PATCH 18/20] Fixing code smells in all animation controllers --- .../components/npc/ArcaneArcherAnimationController.java | 7 +------ .../game/components/npc/CoatAnimationController.java | 6 ------ .../components/npc/DragonKnightAnimationController.java | 6 ------ .../game/components/npc/FireWormAnimationController.java | 6 ------ .../game/components/npc/FirewizardAnimationController.java | 3 --- .../components/npc/NecromancerAnimationController.java | 4 ---- .../game/components/npc/NightBorneAnimationController.java | 5 +---- .../game/components/npc/RockyAnimationController.java | 3 --- .../game/components/npc/SkeletonAnimationController.java | 6 ------ .../game/components/npc/WaterQueenAnimationController.java | 6 ------ .../game/components/npc/WaterSlimeAnimationController.java | 6 ------ .../game/components/npc/WizardAnimationController.java | 6 ------ 12 files changed, 2 insertions(+), 62 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/npc/ArcaneArcherAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/ArcaneArcherAnimationController.java index 71fa5359d..82c587f18 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/ArcaneArcherAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/ArcaneArcherAnimationController.java @@ -5,19 +5,14 @@ import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one * of the events is triggered. */ public class ArcaneArcherAnimationController extends Component { - // // For on collision sounds later - // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; - // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( - // COLLISION_SFX, Sound.class); + AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); private static final String ATTACK_SOUND = "sounds/mobs/archerArrow.mp3"; Sound attackSound = ServiceLocator.getResourceService().getAsset( diff --git a/source/core/src/main/com/csse3200/game/components/npc/CoatAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/CoatAnimationController.java index e9bf8170e..c023e0aa5 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/CoatAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/CoatAnimationController.java @@ -5,19 +5,13 @@ import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one * of the events is triggered. */ public class CoatAnimationController extends Component { - // // For on collision sounds later - // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; - // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( - // COLLISION_SFX, Sound.class); AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); private static final String ATTACK_SOUND = "sounds/mobs/coatAttack.mp3"; Sound attackSound = ServiceLocator.getResourceService().getAsset( diff --git a/source/core/src/main/com/csse3200/game/components/npc/DragonKnightAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/DragonKnightAnimationController.java index 6d9d54215..7302a1cdc 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/DragonKnightAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/DragonKnightAnimationController.java @@ -2,19 +2,13 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one * of the events is triggered. */ public class DragonKnightAnimationController extends Component { - // // For on collision sounds later - // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; - // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( - // COLLISION_SFX, Sound.class); AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); @Override public void create() { diff --git a/source/core/src/main/com/csse3200/game/components/npc/FireWormAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/FireWormAnimationController.java index 141d43d69..28d118210 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/FireWormAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/FireWormAnimationController.java @@ -4,19 +4,13 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one * of the events is triggered. */ public class FireWormAnimationController extends Component { - // // For on collision sounds later - // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; - // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( - // COLLISION_SFX, Sound.class); AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); private static final String ATTACK_SOUND = "sounds/mobs/fireWormRoar.mp3"; Sound attackSound = ServiceLocator.getResourceService().getAsset( diff --git a/source/core/src/main/com/csse3200/game/components/npc/FirewizardAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/FirewizardAnimationController.java index 267c23404..b10236761 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/FirewizardAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/FirewizardAnimationController.java @@ -3,12 +3,9 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; -import java.security.SecureRandom; - public class FirewizardAnimationController extends Component { AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); @Override diff --git a/source/core/src/main/com/csse3200/game/components/npc/NecromancerAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/NecromancerAnimationController.java index 3bdad9407..dfbd4bc9a 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/NecromancerAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/NecromancerAnimationController.java @@ -3,13 +3,9 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; -import java.security.SecureRandom; - public class NecromancerAnimationController extends Component { AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); - @Override public void create() { diff --git a/source/core/src/main/com/csse3200/game/components/npc/NightBorneAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/NightBorneAnimationController.java index d821f6cca..fccdfe580 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/NightBorneAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/NightBorneAnimationController.java @@ -2,7 +2,6 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one @@ -13,9 +12,7 @@ public class NightBorneAnimationController extends Component { // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( // COLLISION_SFX, Sound.class); - AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); - + AnimationRenderComponent animator; @Override public void create() { diff --git a/source/core/src/main/com/csse3200/game/components/npc/RockyAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/RockyAnimationController.java index f6743dd9d..63f850085 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/RockyAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/RockyAnimationController.java @@ -3,11 +3,8 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; -import java.security.SecureRandom; - public class RockyAnimationController extends Component { AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); @Override diff --git a/source/core/src/main/com/csse3200/game/components/npc/SkeletonAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/SkeletonAnimationController.java index d5ea57bfa..7a821ef75 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/SkeletonAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/SkeletonAnimationController.java @@ -4,19 +4,13 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one * of the events is triggered. */ public class SkeletonAnimationController extends Component { - // // For on collision sounds later - // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; - // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( - // COLLISION_SFX, Sound.class); AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); /** Sound variables */ private static final String ATTACK_SOUND = "sounds/mobs/boneBreak.mp3"; diff --git a/source/core/src/main/com/csse3200/game/components/npc/WaterQueenAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/WaterQueenAnimationController.java index 0b8a00b75..294f312b1 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/WaterQueenAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/WaterQueenAnimationController.java @@ -4,19 +4,13 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one * of the events is triggered. */ public class WaterQueenAnimationController extends Component { - // // For on collision sounds later - // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; - // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( - // COLLISION_SFX, Sound.class); AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); private static final String ATTACK_SOUND = "sounds/mobs/waterQueenSpell.mp3"; Sound attackSound = ServiceLocator.getResourceService().getAsset( diff --git a/source/core/src/main/com/csse3200/game/components/npc/WaterSlimeAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/WaterSlimeAnimationController.java index 6909dad10..52163ddcb 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/WaterSlimeAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/WaterSlimeAnimationController.java @@ -2,19 +2,13 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one * of the events is triggered. */ public class WaterSlimeAnimationController extends Component { - // // For on collision sounds later - // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; - // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( - // COLLISION_SFX, Sound.class); AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); @Override public void create() { diff --git a/source/core/src/main/com/csse3200/game/components/npc/WizardAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/WizardAnimationController.java index c8c8e7073..80623d6db 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/WizardAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/WizardAnimationController.java @@ -4,19 +4,13 @@ import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; -import java.security.SecureRandom; /** * This class listens to events relevant to a ghost entity's state and plays the animation when one * of the events is triggered. */ public class WizardAnimationController extends Component { - // // For on collision sounds later - // private static final String COLLISION_SFX = "sounds/projectiles/on_collision.mp3"; - // Sound onCollisionSound = ServiceLocator.getResourceService().getAsset( - // COLLISION_SFX, Sound.class); AnimationRenderComponent animator; - private SecureRandom rand = new SecureRandom(); /** Sound variables */ private static final String ATTACK_SOUND = "sounds/mobs/wizardSpell.mp3"; From aa34129e2fd21a39f1113ba30bac8ccce9c01327 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Tue, 17 Oct 2023 09:57:34 +1000 Subject: [PATCH 19/20] Fixing code smell in wizard animation controller --- .../csse3200/game/components/npc/WizardAnimationController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/npc/WizardAnimationController.java b/source/core/src/main/com/csse3200/game/components/npc/WizardAnimationController.java index 80623d6db..9b4660afb 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/WizardAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/npc/WizardAnimationController.java @@ -25,8 +25,6 @@ public void create() { entity.getEvents().addListener("mob_walk", this::animateWalk); entity.getEvents().addListener("mob_attack", this::animateAttack); entity.getEvents().addListener("mob_death", this::animateDeath); - - } void animateWalk() { From 5c86d9b97e39260575edf9cf5946088556b890f1 Mon Sep 17 00:00:00 2001 From: gregchan550 <86044792+gregchan550@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:39:22 +1000 Subject: [PATCH 20/20] set normal speed for bosses and general mobs so slow and stun works properly --- .../com/csse3200/game/components/tasks/MobTask/MobTask.java | 3 ++- .../csse3200/game/components/tasks/bosstask/IceBabyTask.java | 1 + .../csse3200/game/components/tasks/bosstask/PatrickTask.java | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java b/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java index 2cc103bd8..9eebbf1d4 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java @@ -89,7 +89,6 @@ public void start() { super.start(); mob = owner.getEntity(); animation = mob.getComponent(AnimationRenderComponent.class); - mob.getComponent(PhysicsMovementComponent.class).setSpeed(MELEE_MOB_SPEED); melee = mobType.isMelee(); movementTask = new MovementTask(new Vector2(0f, mob.getPosition().y)); @@ -102,8 +101,10 @@ public void start() { if (melee) { mob.getComponent(PhysicsMovementComponent.class).setSpeed(MELEE_MOB_SPEED); + mob.getComponent(PhysicsMovementComponent.class).setNormalSpeed(MELEE_MOB_SPEED); } else { mob.getComponent(PhysicsMovementComponent.class).setSpeed(MELEE_RANGE_SPEED); + mob.getComponent(PhysicsMovementComponent.class).setNormalSpeed(MELEE_RANGE_SPEED); } } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java index 4b298c059..7cfea58df 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java @@ -71,6 +71,7 @@ public void start() { animation = iceBaby.getComponent(AnimationRenderComponent.class); currentPos = iceBaby.getPosition(); iceBaby.getComponent(PhysicsMovementComponent.class).setSpeed(ICEBABY_SPEED); + iceBaby.getComponent(PhysicsMovementComponent.class).setNormalSpeed(ICEBABY_SPEED); Timer.schedule(new Timer.Task() { @Override public void run() { diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bosstask/PatrickTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bosstask/PatrickTask.java index bf5e6c30c..cb3f27b8f 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/bosstask/PatrickTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/bosstask/PatrickTask.java @@ -64,6 +64,7 @@ public void start() { patrick = owner.getEntity(); animation = owner.getEntity().getComponent(AnimationRenderComponent.class); // get animation patrick.getComponent(PhysicsMovementComponent.class).setSpeed(PATRICK_SPEED); // set speed + patrick.getComponent(PhysicsMovementComponent.class).setNormalSpeed(PATRICK_SPEED); // give game time to load Timer.schedule(new Timer.Task() {