From 0dddd1283bfada3f3ebde802bab0234e814f7cad Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Thu, 31 Aug 2023 19:55:16 +1000 Subject: [PATCH 01/72] Created new TowerUpgraderComponent class, which listens for an event to make an upgrade to the tower. No upgrade types do anything yet. --- .../tower/TowerUpgraderComponent.java | 32 +++++++++++++++++++ .../tower/TowerUpgradeComponentTest.java | 30 +++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java create mode 100644 source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java diff --git a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java new file mode 100644 index 000000000..a88209178 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java @@ -0,0 +1,32 @@ +package com.csse3200.game.components.tower; + +import com.csse3200.game.components.Component; + +/** + * Listens for an event from the popup menu to upgrade + * the turret entity this component is attached to. + */ +public class TowerUpgraderComponent extends Component { + public enum UPGRADE { + ATTACK, MAXHP, FIRERATE + } + + @Override + public void create() { + super.create(); + entity.getEvents().addListener("upgradeTower", this::upgradeTower); + } + + /** + * Determines which type of upgrade to perform based on arguments provided by the event trigger. + * @param upgradeType An enum indicating the type of upgrade to do + * @param value How much the upgrade should change the tower's stats, where applicable + */ + void upgradeTower(UPGRADE upgradeType, int value) { + switch (upgradeType) { + case ATTACK -> {/*Not implemented yet*/} + case MAXHP -> {/*Not implemented yet either*/} + case FIRERATE -> {/*Not implemented at the present moment*/} + } + } +} diff --git a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java new file mode 100644 index 000000000..df008d2ed --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java @@ -0,0 +1,30 @@ +package com.csse3200.game.components.tower; + +import com.csse3200.game.components.tower.TowerUpgraderComponent; +import com.csse3200.game.input.InputService; +import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.events.EventHandler; +import com.csse3200.game.events.listeners.EventListener2; +import com.csse3200.game.extensions.GameExtension; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import static org.mockito.Mockito.*; + +@ExtendWith(GameExtension.class) +class TowerUpgradeComponentTest { + + @BeforeEach + @Test + void doesNotCrash() { + Entity entity = new Entity(); + InputService inputService = new InputService(); + ServiceLocator.registerInputService(inputService); + TowerUpgraderComponent towerUpgraderComponent = spy(TowerUpgraderComponent.class); + entity.addComponent(towerUpgraderComponent); + entity.create(); + entity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.ATTACK, 0); + verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.ATTACK, 0); + } +} From 9cde24aece2a55cfa79a3870fa96f524b08ef15e Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Thu, 31 Aug 2023 20:19:21 +1000 Subject: [PATCH 02/72] Added capabilty for TowerUpgraderComponent to increase the the base attack stat in the CombatStatsComponent of the same entity. --- .../tower/TowerUpgraderComponent.java | 14 +++++++++++- .../tower/TowerUpgradeComponentTest.java | 22 +++++++++---------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java index a88209178..de078bff4 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java +++ b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java @@ -1,5 +1,6 @@ package com.csse3200.game.components.tower; +import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.Component; /** @@ -19,14 +20,25 @@ public void create() { /** * Determines which type of upgrade to perform based on arguments provided by the event trigger. + * * @param upgradeType An enum indicating the type of upgrade to do * @param value How much the upgrade should change the tower's stats, where applicable */ void upgradeTower(UPGRADE upgradeType, int value) { switch (upgradeType) { - case ATTACK -> {/*Not implemented yet*/} + case ATTACK -> {upgradeTowerAttack(value);} case MAXHP -> {/*Not implemented yet either*/} case FIRERATE -> {/*Not implemented at the present moment*/} } } + + /** + * Increases the tower's attack stat. + * + * @param increase The amount that the attack stat should increase by. + */ + void upgradeTowerAttack(int increase) { + int oldAttack = getEntity().getComponent(CombatStatsComponent.class).getBaseAttack(); + getEntity().getComponent(CombatStatsComponent.class).setBaseAttack(oldAttack + increase); + } } diff --git a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java index df008d2ed..5397a8184 100644 --- a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java @@ -1,30 +1,30 @@ package com.csse3200.game.components.tower; -import com.csse3200.game.components.tower.TowerUpgraderComponent; -import com.csse3200.game.input.InputService; -import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.entities.Entity; -import com.csse3200.game.events.EventHandler; -import com.csse3200.game.events.listeners.EventListener2; import com.csse3200.game.extensions.GameExtension; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @ExtendWith(GameExtension.class) class TowerUpgradeComponentTest { + Entity entity; @BeforeEach + void beforeEach() {entity = new Entity();} + @Test - void doesNotCrash() { - Entity entity = new Entity(); - InputService inputService = new InputService(); - ServiceLocator.registerInputService(inputService); + void increaseAttackStat() { TowerUpgraderComponent towerUpgraderComponent = spy(TowerUpgraderComponent.class); + CombatStatsComponent combatStatsComponent = new CombatStatsComponent(100,10); entity.addComponent(towerUpgraderComponent); + entity.addComponent(combatStatsComponent); entity.create(); - entity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.ATTACK, 0); - verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.ATTACK, 0); + entity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.ATTACK, 10); + verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.ATTACK, 10); + assertEquals(20, combatStatsComponent.getBaseAttack()); } } From 98c3dc545b0943bdbb9c108cdb04d6855a6a3576 Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Sun, 3 Sep 2023 18:04:55 +1000 Subject: [PATCH 03/72] Fixed filename typo --- .../com/csse3200/game/entities/factories/ProjectileFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index ff861fdb4..83f2093b9 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -24,7 +24,7 @@ public class ProjectileFactory { private static final NPCConfigs configs = - FileLoader.readClass(NPCConfigs.class, "configs/NPCS.json"); + FileLoader.readClass(NPCConfigs.class, "configs/NPCs.json"); /** * Creates a fireball Entity. From c75f7826cb40af19b9502c0e6049bb1988e6e901 Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Sun, 3 Sep 2023 18:33:32 +1000 Subject: [PATCH 04/72] Implemented the upgrading of a tower's max health, including changes to CombatStatsComponent to alter the fullHealth variable. --- .../game/components/CombatStatsComponent.java | 21 ++++++++++++++++++- .../tower/TowerUpgraderComponent.java | 13 +++++++++++- ...t.java => TowerUpgraderComponentTest.java} | 14 ++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) rename source/core/src/test/com/csse3200/game/components/tower/{TowerUpgradeComponentTest.java => TowerUpgraderComponentTest.java} (63%) diff --git a/source/core/src/main/com/csse3200/game/components/CombatStatsComponent.java b/source/core/src/main/com/csse3200/game/components/CombatStatsComponent.java index a860b6547..5b1ef208b 100644 --- a/source/core/src/main/com/csse3200/game/components/CombatStatsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/CombatStatsComponent.java @@ -28,7 +28,7 @@ public class CombatStatsComponent extends Component { private static final Logger logger = LoggerFactory.getLogger(CombatStatsComponent.class); private int health; private int baseAttack; - private final int fullHealth; + private int fullHealth; private String state; private ArrayList drops; private ArrayList closeRangeAbilities; @@ -99,6 +99,25 @@ public void addHealth(int health) { changeState(); } + /** + * Returns the entity's fullHealth value (note that this does not influence the ability to set its actual health) + * + * @return The entity's fullHealth variable + */ + public int getMaxHealth() { + return fullHealth; + } + + /** + * Sets the entity's fullHealth variable. + * Intended for when the entity's maximum health must be changed after creation, like upgrading a turret's HP. + * + * @param newMaxHealth The new value fullHealth should be set to + */ + public void setMaxHealth(int newMaxHealth) { + fullHealth = newMaxHealth; + } + /** * Returns the entity's base attack damage. * diff --git a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java index de078bff4..12d2b4660 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java +++ b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java @@ -27,7 +27,7 @@ public void create() { void upgradeTower(UPGRADE upgradeType, int value) { switch (upgradeType) { case ATTACK -> {upgradeTowerAttack(value);} - case MAXHP -> {/*Not implemented yet either*/} + case MAXHP -> {upgradeTowerMaxHealth(value);} case FIRERATE -> {/*Not implemented at the present moment*/} } } @@ -41,4 +41,15 @@ void upgradeTowerAttack(int increase) { int oldAttack = getEntity().getComponent(CombatStatsComponent.class).getBaseAttack(); getEntity().getComponent(CombatStatsComponent.class).setBaseAttack(oldAttack + increase); } + + /** + * Increases the tower's maximum health, and restores the tower's health to the new maximum. + * + * @param increase The amount that the max health stat should increase by. + */ + void upgradeTowerMaxHealth(int increase) { + int oldMaxHealth = getEntity().getComponent(CombatStatsComponent.class).getMaxHealth(); + getEntity().getComponent(CombatStatsComponent.class).setMaxHealth(oldMaxHealth + increase); + getEntity().getComponent(CombatStatsComponent.class).setHealth(oldMaxHealth + increase); + } } diff --git a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java similarity index 63% rename from source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java rename to source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java index 5397a8184..a72c89184 100644 --- a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgradeComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java @@ -10,7 +10,7 @@ import static org.mockito.Mockito.*; @ExtendWith(GameExtension.class) -class TowerUpgradeComponentTest { +class TowerUpgraderComponentTest { Entity entity; @BeforeEach @@ -27,4 +27,16 @@ void increaseAttackStat() { verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.ATTACK, 10); assertEquals(20, combatStatsComponent.getBaseAttack()); } + + @Test + void increaseMaxHealthStat() { + TowerUpgraderComponent towerUpgraderComponent = spy(TowerUpgraderComponent.class); + CombatStatsComponent combatStatsComponent = new CombatStatsComponent(100,10); + entity.addComponent(towerUpgraderComponent); + entity.addComponent(combatStatsComponent); + entity.create(); + entity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.MAXHP, 50); + verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.MAXHP, 50); + assertEquals(150, combatStatsComponent.getMaxHealth()); + } } From 051b6b236678c7dc7e6ec3179405e993f806a4e4 Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 4 Sep 2023 13:49:11 +1000 Subject: [PATCH 05/72] added new methods and config files for tower1 and tower2 --- .../main/com/csse3200/game/entities/configs/Tower1Config.java | 2 ++ .../main/com/csse3200/game/entities/configs/Tower2Config.java | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java create mode 100644 source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java diff --git a/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java b/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java new file mode 100644 index 000000000..730e7c610 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java @@ -0,0 +1,2 @@ +package com.csse3200.game.entities.configs;public class Tower1Config { +} diff --git a/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java b/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java new file mode 100644 index 000000000..fb7f8a560 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java @@ -0,0 +1,2 @@ +package com.csse3200.game.entities.configs;public class Tower2Config { +} From 71079eea006e3187b8e69d2322e264c86c8e1dfa Mon Sep 17 00:00:00 2001 From: Shivam Date: Mon, 4 Sep 2023 13:51:23 +1000 Subject: [PATCH 06/72] Added the modified files --- source/core/assets/configs/tower.json | 10 +++++++ .../game/entities/configs/Tower1Config.java | 7 ++++- .../game/entities/configs/Tower2Config.java | 7 ++++- .../entities/configs/baseTowerConfigs.java | 2 ++ .../game/entities/factories/TowerFactory.java | 29 ++++++++++++++++--- 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/source/core/assets/configs/tower.json b/source/core/assets/configs/tower.json index 004dda33c..d5d255ff7 100644 --- a/source/core/assets/configs/tower.json +++ b/source/core/assets/configs/tower.json @@ -13,5 +13,15 @@ "health": 20, "baseAttack": 0, "cost": 1 + }, + "tower1": { + "health": 10, + "baseAttack": 10, + "cost": 10 + }, + "tower2": { + "health": 10, + "baseAttack": 10, + "cost": 10 } } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java b/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java index 730e7c610..9c8f844c7 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java @@ -1,2 +1,7 @@ -package com.csse3200.game.entities.configs;public class Tower1Config { +package com.csse3200.game.entities.configs; + +public class Tower1Config { + public int health = 1; + public int baseAttack = 0; + public int cost = 1; } diff --git a/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java b/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java index fb7f8a560..b471e8166 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java @@ -1,2 +1,7 @@ -package com.csse3200.game.entities.configs;public class Tower2Config { +package com.csse3200.game.entities.configs; + +public class Tower2Config { + public int health = 1; + public int baseAttack = 0; + public int cost = 1; } diff --git a/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java index 13c6fe19b..1e3998e56 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java @@ -7,4 +7,6 @@ public class baseTowerConfigs { public WeaponTowerConfig weapon = new WeaponTowerConfig(); public WallTowerConfig wall = new WallTowerConfig(); public IncomeTowerConfig income = new IncomeTowerConfig(); + public Tower1Config tower1 = new Tower1Config(); + public Tower2Config tower2 = new Tower2Config(); } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index 77b29bb45..72d9f7941 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -1,5 +1,7 @@ package com.csse3200.game.entities.factories; +import com.csse3200.game.entities.Weapon; +import com.csse3200.game.entities.configs.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.badlogic.gdx.graphics.Texture; @@ -13,14 +15,10 @@ import com.csse3200.game.components.tower.TowerAnimationController; import com.csse3200.game.components.tasks.CurrencyTask; import com.csse3200.game.entities.Entity; -import com.csse3200.game.entities.configs.WallTowerConfig; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.components.ColliderComponent; import com.csse3200.game.physics.components.HitboxComponent; import com.csse3200.game.physics.components.PhysicsComponent; -import com.csse3200.game.entities.configs.WeaponTowerConfig; -import com.csse3200.game.entities.configs.IncomeTowerConfig; -import com.csse3200.game.entities.configs.baseTowerConfigs; import com.csse3200.game.files.FileLoader; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.rendering.TextureRenderComponent; @@ -121,6 +119,29 @@ public static Entity createWeaponTower() { return weapon; } + + public static Entity createWeaponTower1() { + Entity weaponTower = createBaseTower(); + Tower1Config config = configs.tower1; + + weaponTower + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + .addComponent(new CostComponent(config.cost)); + + return weaponTower; + } + + public static Entity createWeaponTower2() { + Entity weaponTower = createBaseTower(); + Tower2Config config = configs.tower2; + + weaponTower + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + .addComponent((new CostComponent(config.cost))); + + return weaponTower; + } + /** * Creates a generic tower entity to be used as a base entity by more specific tower creation methods. * @return entity From 3ebc05571a660e0e0041ba7a6e0fdf8ef0f9785b Mon Sep 17 00:00:00 2001 From: Shivam Date: Wed, 6 Sep 2023 13:36:25 +1000 Subject: [PATCH 07/72] renamed tower1 to fireTower and tower2 with stunTower in relevant files --- source/core/assets/configs/tower.json | 4 +-- ...Tower1Config.java => FireTowerConfig.java} | 2 +- ...Tower2Config.java => StunTowerConfig.java} | 2 +- .../entities/configs/baseTowerConfigs.java | 4 +-- .../game/entities/factories/TowerFactory.java | 25 ++++++++----------- 5 files changed, 16 insertions(+), 21 deletions(-) rename source/core/src/main/com/csse3200/game/entities/configs/{Tower1Config.java => FireTowerConfig.java} (80%) rename source/core/src/main/com/csse3200/game/entities/configs/{Tower2Config.java => StunTowerConfig.java} (80%) diff --git a/source/core/assets/configs/tower.json b/source/core/assets/configs/tower.json index d5d255ff7..b11cf27c6 100644 --- a/source/core/assets/configs/tower.json +++ b/source/core/assets/configs/tower.json @@ -14,12 +14,12 @@ "baseAttack": 0, "cost": 1 }, - "tower1": { + "fireTower": { "health": 10, "baseAttack": 10, "cost": 10 }, - "tower2": { + "stunTower": { "health": 10, "baseAttack": 10, "cost": 10 diff --git a/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java b/source/core/src/main/com/csse3200/game/entities/configs/FireTowerConfig.java similarity index 80% rename from source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java rename to source/core/src/main/com/csse3200/game/entities/configs/FireTowerConfig.java index 9c8f844c7..7e697040b 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/Tower1Config.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/FireTowerConfig.java @@ -1,6 +1,6 @@ package com.csse3200.game.entities.configs; -public class Tower1Config { +public class FireTowerConfig { public int health = 1; public int baseAttack = 0; public int cost = 1; diff --git a/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java b/source/core/src/main/com/csse3200/game/entities/configs/StunTowerConfig.java similarity index 80% rename from source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java rename to source/core/src/main/com/csse3200/game/entities/configs/StunTowerConfig.java index b471e8166..fc711e70f 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/Tower2Config.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/StunTowerConfig.java @@ -1,6 +1,6 @@ package com.csse3200.game.entities.configs; -public class Tower2Config { +public class StunTowerConfig { public int health = 1; public int baseAttack = 0; public int cost = 1; diff --git a/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java index 1e3998e56..f7549d4f4 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java @@ -7,6 +7,6 @@ public class baseTowerConfigs { public WeaponTowerConfig weapon = new WeaponTowerConfig(); public WallTowerConfig wall = new WallTowerConfig(); public IncomeTowerConfig income = new IncomeTowerConfig(); - public Tower1Config tower1 = new Tower1Config(); - public Tower2Config tower2 = new Tower2Config(); + public FireTowerConfig fireTower = new FireTowerConfig(); + public StunTowerConfig stunTower = new StunTowerConfig(); } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index 72d9f7941..595965d72 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -1,10 +1,6 @@ package com.csse3200.game.entities.factories; -import com.csse3200.game.entities.Weapon; import com.csse3200.game.entities.configs.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.physics.box2d.BodyDef.BodyType; @@ -120,26 +116,25 @@ public static Entity createWeaponTower() { } - public static Entity createWeaponTower1() { - Entity weaponTower = createBaseTower(); - Tower1Config config = configs.tower1; - - weaponTower + public static Entity createFireTower() { + Entity fireTower = createBaseTower(); + FireTowerConfig config = configs.fireTower; + fireTower .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) .addComponent(new CostComponent(config.cost)); - return weaponTower; + return fireTower; } - public static Entity createWeaponTower2() { - Entity weaponTower = createBaseTower(); - Tower2Config config = configs.tower2; + public static Entity createStunTower() { + Entity stunTower = createBaseTower(); + StunTowerConfig config = configs.stunTower; - weaponTower + stunTower .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) .addComponent((new CostComponent(config.cost))); - return weaponTower; + return stunTower; } /** From 82715be1ede319429f42b286891ccd361d1ca004 Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Wed, 6 Sep 2023 13:58:52 +1000 Subject: [PATCH 08/72] Added fire rate upgrade functionality to TowerUpgraderComponent, also added functionality to TowerCombatTask for supporting fire rate upgrades. --- .../components/tasks/TowerCombatTask.java | 44 ++++++++++++++++++- .../tower/TowerUpgraderComponent.java | 6 ++- .../tower/TowerUpgraderComponentTest.java | 33 +++++++++++--- 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index bae13b0fd..b426f70ae 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -10,6 +10,7 @@ import com.csse3200.game.physics.raycast.RaycastHit; import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; +import static java.lang.Math.round; /** * The TowerCombatTask runs the AI for the WeaponTower class. The tower will scan for targets in a straight line @@ -28,6 +29,7 @@ public class TowerCombatTask extends DefaultTask implements PriorityTask { // class attributes private final int priority; // The active priority this task will have + private float fireRateInterval; // time interval to fire projectiles at enemies in seconds private final float maxRange; private Vector2 towerPosition = new Vector2(10, 10); // initial placeholder value - will be overwritten private final Vector2 maxRangePosition = new Vector2(); @@ -48,6 +50,20 @@ private enum STATE { public TowerCombatTask(int priority, float maxRange) { this.priority = priority; this.maxRange = maxRange; + this.fireRateInterval = 1; + physics = ServiceLocator.getPhysicsService().getPhysics(); + timeSource = ServiceLocator.getTimeSource(); + } + + /** + * @param priority Task priority when targets are detected (0 when nothing detected). Must be a positive integer. + * @param maxRange Maximum effective range of the weapon tower. This determines the detection distance of targets + * @param fireRate The number of times per second this tower should fire its weapon + */ + public TowerCombatTask(int priority, float maxRange, float fireRate) { + this.priority = priority; + this.maxRange = maxRange; + this.fireRateInterval = 1/fireRate; physics = ServiceLocator.getPhysicsService().getPhysics(); timeSource = ServiceLocator.getTimeSource(); } @@ -63,6 +79,8 @@ public void start() { this.maxRangePosition.set(towerPosition.x + maxRange, towerPosition.y); // Default to idle mode owner.getEntity().getEvents().trigger(IDLE); + // Set up listener to change firerate + owner.getEntity().getEvents().addListener("addFireRate",this::changeFireRateInterval); endTime = timeSource.getTime() + (INTERVAL * 500); } @@ -75,7 +93,11 @@ public void start() { public void update() { if (timeSource.getTime() >= endTime) { updateTowerState(); - endTime = timeSource.getTime() + (INTERVAL * 1000); + if (towerState == STATE.FIRING) { + endTime = timeSource.getTime() + round(fireRateInterval * 1000); + } else { + endTime = timeSource.getTime() + (INTERVAL * 1000); + } } } @@ -172,4 +194,24 @@ private boolean isTargetVisible() { // If there is an obstacle in the path to the max range point, mobs visible. return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); } + + /** + * Increases the fireRateInterval, changing how frequently the turret fires. Will decrease if the argument is negative. + * + * @param perMinute The number of times per minute the turret's fire rate should increase + */ + private void changeFireRateInterval(int perMinute) { + float oldFireSpeed = 1/fireRateInterval; + float newFireSpeed = oldFireSpeed + perMinute/60f; + fireRateInterval = 1/newFireSpeed; + } + + /** + * Function for getting the turret's fire rate. + * + * @return The fireRateInterval variable + */ + public float getFireRateInterval() { + return fireRateInterval; + } } diff --git a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java index 12d2b4660..789f31d39 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java +++ b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java @@ -2,6 +2,7 @@ import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.Component; +import static java.lang.Math.round; /** * Listens for an event from the popup menu to upgrade @@ -20,15 +21,16 @@ public void create() { /** * Determines which type of upgrade to perform based on arguments provided by the event trigger. + * Note: The fire rate upgrade is in shots per minute. * * @param upgradeType An enum indicating the type of upgrade to do - * @param value How much the upgrade should change the tower's stats, where applicable + * @param value How much the upgrade should change the tower's stats */ void upgradeTower(UPGRADE upgradeType, int value) { switch (upgradeType) { case ATTACK -> {upgradeTowerAttack(value);} case MAXHP -> {upgradeTowerMaxHealth(value);} - case FIRERATE -> {/*Not implemented at the present moment*/} + case FIRERATE -> {getEntity().getEvents().trigger("addFireRate", value);} } } diff --git a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java index a72c89184..f9ab79901 100644 --- a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java @@ -1,8 +1,13 @@ package com.csse3200.game.components.tower; +import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.components.tasks.TowerCombatTask; import com.csse3200.game.entities.Entity; import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -12,14 +17,18 @@ @ExtendWith(GameExtension.class) class TowerUpgraderComponentTest { Entity entity; + TowerUpgraderComponent towerUpgraderComponent; + CombatStatsComponent combatStatsComponent; @BeforeEach - void beforeEach() {entity = new Entity();} + void beforeEach() { + entity = new Entity(); + towerUpgraderComponent = spy(TowerUpgraderComponent.class); + combatStatsComponent = new CombatStatsComponent(100,10); + } @Test void increaseAttackStat() { - TowerUpgraderComponent towerUpgraderComponent = spy(TowerUpgraderComponent.class); - CombatStatsComponent combatStatsComponent = new CombatStatsComponent(100,10); entity.addComponent(towerUpgraderComponent); entity.addComponent(combatStatsComponent); entity.create(); @@ -30,8 +39,6 @@ void increaseAttackStat() { @Test void increaseMaxHealthStat() { - TowerUpgraderComponent towerUpgraderComponent = spy(TowerUpgraderComponent.class); - CombatStatsComponent combatStatsComponent = new CombatStatsComponent(100,10); entity.addComponent(towerUpgraderComponent); entity.addComponent(combatStatsComponent); entity.create(); @@ -39,4 +46,20 @@ void increaseMaxHealthStat() { verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.MAXHP, 50); assertEquals(150, combatStatsComponent.getMaxHealth()); } + + @Test + void increaseFireRate() { + entity.addComponent(towerUpgraderComponent); + AITaskComponent aiTaskComponent = new AITaskComponent(); + ServiceLocator.registerPhysicsService(mock(PhysicsService.class)); + ServiceLocator.registerTimeSource(mock(GameTime.class)); + TowerCombatTask towerCombatTask = new TowerCombatTask(10, 10, 1); + aiTaskComponent.addTask(towerCombatTask); + entity.addComponent(aiTaskComponent); + towerCombatTask.start(); + entity.create(); + entity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.FIRERATE, 60); + verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.FIRERATE, 60); + assertEquals(0.5, towerCombatTask.getFireRateInterval()); + } } From 705c3e24bd409dc98778a3f359462e3ddeac7742 Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Wed, 6 Sep 2023 14:33:46 +1000 Subject: [PATCH 09/72] TowerUpgraderComponent is now added to tower entities when they are created. --- .../com/csse3200/game/entities/factories/TowerFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index 77b29bb45..366b0c0f1 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -1,5 +1,6 @@ package com.csse3200.game.entities.factories; +import com.csse3200.game.components.tower.TowerUpgraderComponent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.badlogic.gdx.graphics.Texture; @@ -116,7 +117,8 @@ public static Entity createWeaponTower() { .addComponent(new CostComponent(config.cost)) .addComponent(aiTaskComponent) .addComponent(animator) - .addComponent(new TowerAnimationController()); + .addComponent(new TowerAnimationController()) + .addComponent(new TowerUpgraderComponent()); return weapon; From 7c9e95f655fdb7d73d4711fe5f68eac00beac6cf Mon Sep 17 00:00:00 2001 From: Thivan W Date: Wed, 6 Sep 2023 14:48:42 +1000 Subject: [PATCH 10/72] added range boss's ranged attack during pause --- .../components/tasks/RangeBossMovementTask.java | 15 +++++++++++---- .../game/entities/factories/BossKingFactory.java | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java index 15db419ea..6518d00e7 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java @@ -4,6 +4,9 @@ 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.entities.Entity; +import com.csse3200.game.entities.factories.ProjectileFactory; +import com.csse3200.game.services.ServiceLocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,7 +18,7 @@ public class RangeBossMovementTask extends DefaultTask implements PriorityTask { private static final Logger logger = LoggerFactory.getLogger(RangeBossMovementTask.class); private final float waitTime; - private Vector2 startPos; + private Vector2 currentPos; private MovementTask movementTask; private WaitTask waitTask; private Task currentTask; @@ -36,11 +39,11 @@ public int getPriority() { @Override public void start() { super.start(); - startPos = owner.getEntity().getPosition(); + currentPos = owner.getEntity().getPosition(); waitTask = new WaitTask(waitTime); waitTask.create(owner); - movementTask = new MovementTask(startPos.sub(2,0)); + movementTask = new MovementTask(currentPos.sub(2,0)); movementTask.create(owner); movementTask.start(); @@ -54,6 +57,10 @@ public void start() { public void update() { if (currentTask.getStatus() != Status.ACTIVE) { if (currentTask == movementTask) { + Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), new Vector2(0, (currentPos.y + 0.75f)), new Vector2(2f,2f)); + newProjectile.scaleHeight(-0.4f); + newProjectile.setPosition((float) (currentPos.x), (float) (currentPos.y+0.75f)); + ServiceLocator.getEntityService().register(newProjectile); startWaiting(); } else { startMoving(); @@ -69,7 +76,7 @@ private void startWaiting() { private void startMoving() { logger.debug("Starting moving"); - movementTask.setTarget(startPos.sub(2,0)); + movementTask.setTarget(currentPos.sub(2,0)); swapTask(movementTask); } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/BossKingFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/BossKingFactory.java index 1bd29a628..23b840298 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/BossKingFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/BossKingFactory.java @@ -61,8 +61,8 @@ public static Entity createBossKing1(Entity target) { .addComponent(new BossAnimationController()); bossKing1.getComponent(AnimationRenderComponent.class).scaleEntity(); - bossKing1.scaleHeight(0.5f); - bossKing1.scaleWidth(0.5f); + bossKing1.setScale(-1f,1f); + return bossKing1; } From 23d4e6e588f65be742a7989bc54e80b257dba204 Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Wed, 6 Sep 2023 15:01:19 +1000 Subject: [PATCH 11/72] Added repair capability to TowerUpgraderComponent --- .../components/tower/TowerUpgraderComponent.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java index 789f31d39..07ead7edc 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java +++ b/source/core/src/main/com/csse3200/game/components/tower/TowerUpgraderComponent.java @@ -10,7 +10,7 @@ */ public class TowerUpgraderComponent extends Component { public enum UPGRADE { - ATTACK, MAXHP, FIRERATE + ATTACK, MAXHP, FIRERATE, REPAIR } @Override @@ -24,13 +24,14 @@ public void create() { * Note: The fire rate upgrade is in shots per minute. * * @param upgradeType An enum indicating the type of upgrade to do - * @param value How much the upgrade should change the tower's stats + * @param value How much the upgrade should change the tower's stats, if applicable */ void upgradeTower(UPGRADE upgradeType, int value) { switch (upgradeType) { case ATTACK -> {upgradeTowerAttack(value);} case MAXHP -> {upgradeTowerMaxHealth(value);} case FIRERATE -> {getEntity().getEvents().trigger("addFireRate", value);} + case REPAIR -> {repairTower();} } } @@ -54,4 +55,12 @@ void upgradeTowerMaxHealth(int increase) { getEntity().getComponent(CombatStatsComponent.class).setMaxHealth(oldMaxHealth + increase); getEntity().getComponent(CombatStatsComponent.class).setHealth(oldMaxHealth + increase); } + + /** + * Restores the tower's health to its maximum health. + */ + void repairTower() { + int maxHealth = getEntity().getComponent(CombatStatsComponent.class).getMaxHealth(); + getEntity().getComponent(CombatStatsComponent.class).setHealth(maxHealth); + } } From 0b5452e2112c807ccc7bcd6da1f4f4b38223e148 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Thu, 7 Sep 2023 15:38:08 +1000 Subject: [PATCH 12/72] fixed bug (#104) --- .../com/csse3200/game/rendering/AnimationRenderComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/rendering/AnimationRenderComponent.java b/source/core/src/main/com/csse3200/game/rendering/AnimationRenderComponent.java index 8f52f18bb..0143567f3 100644 --- a/source/core/src/main/com/csse3200/game/rendering/AnimationRenderComponent.java +++ b/source/core/src/main/com/csse3200/game/rendering/AnimationRenderComponent.java @@ -179,7 +179,7 @@ protected void draw(SpriteBatch batch) { @Override public void dispose() { - // atlas.dispose(); + // atlas.dispose(); // this has to be disabled to keep the atlas file for other entities that rely on it super.dispose(); } } From e46afceb4568bed0f8ecfab601988b2641757b6d Mon Sep 17 00:00:00 2001 From: Shivam Date: Thu, 7 Sep 2023 19:07:37 +1000 Subject: [PATCH 13/72] created the FireTowerAnimationController, FireTowerCombatTask and completed the createFireTower method in TowerFactory. --- .../images/towers/fire_tower_atlas.atlas | 83 ++++++++++++ .../assets/images/towers/fire_tower_atlas.png | Bin 0 -> 8037 bytes .../csse3200/game/areas/ForestGameArea.java | 4 + .../components/tasks/FireTowerCombatTask.java | 126 ++++++++++++++++++ .../tower/FireTowerAnimationController.java | 42 ++++++ .../game/entities/factories/TowerFactory.java | 28 +++- 6 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 source/core/assets/images/towers/fire_tower_atlas.atlas create mode 100644 source/core/assets/images/towers/fire_tower_atlas.png create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java create mode 100644 source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java diff --git a/source/core/assets/images/towers/fire_tower_atlas.atlas b/source/core/assets/images/towers/fire_tower_atlas.atlas new file mode 100644 index 000000000..80fa90a9f --- /dev/null +++ b/source/core/assets/images/towers/fire_tower_atlas.atlas @@ -0,0 +1,83 @@ + +fire_tower_atlas.png +size: 2048, 256 +format: RGBA8888 +filter: Nearest, Nearest +repeat: none +attac_prep + rotate: false + xy: 2, 2 + size: 159, 150 + orig: 159, 150 + offset: 0, 0 + index: 2 +attack + rotate: false + xy: 467, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 1 +attack + rotate: false + xy: 923, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 3 +attack + rotate: false + xy: 1075, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 0 +attack + rotate: false + xy: 1379, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 2 +attack_prep + rotate: false + xy: 315, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 0 +idle + rotate: false + xy: 163, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 1 +attack_prep + rotate: false + xy: 163, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 1 +idle + rotate: false + xy: 619, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 3 +idle + rotate: false + xy: 771, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 0 +idle + rotate: false + xy: 1227, 2 + size: 150, 150 + orig: 150, 150 + offset: 0, 0 + index: 2 diff --git a/source/core/assets/images/towers/fire_tower_atlas.png b/source/core/assets/images/towers/fire_tower_atlas.png new file mode 100644 index 0000000000000000000000000000000000000000..cbdce58d1a7ef6309bca48b921303229e0648956 GIT binary patch literal 8037 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 literal 0 HcmV?d00001 diff --git a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java index c3e93e6ef..d1aedf83c 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -68,6 +68,7 @@ public class ForestGameArea extends GameArea { "images/towers/turret.png", "images/towers/turret01.png", "images/towers/turret_deployed.png", + "images/towers/fire_tower_atlas.png", "images/background/building2.png", "images/mobs/robot.png", "images/mobs/Attack_1.png", @@ -92,6 +93,7 @@ public class ForestGameArea extends GameArea { "images/ghostKing.atlas", "images/towers/turret.atlas", "images/towers/turret01.atlas", + "images/towers/fire_tower_atlas.atlas", "images/mobs/xenoGruntRunning.atlas", "images/mobs/robot.atlas", "images/mobs/rangeBossRight.atlas" @@ -393,7 +395,9 @@ private void spawnWeaponTower() { GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); Entity weaponTower = TowerFactory.createWeaponTower(); Entity wallTower = TowerFactory.createWallTower(); + Entity fireTower = TowerFactory.createFireTower(); spawnEntityAt(weaponTower, randomPos, true, true); + spawnEntityAt(fireTower, randomPos, true, true); spawnEntityAt(wallTower, new GridPoint2(randomPos.x + 3, randomPos.y), true, true); } } 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 new file mode 100644 index 000000000..d2c2361f9 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/FireTowerCombatTask.java @@ -0,0 +1,126 @@ +package com.csse3200.game.components.tasks; + +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.ProjectileFactory; +import com.csse3200.game.physics.PhysicsEngine; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.raycast.RaycastHit; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; + +/** + * The FireTowerCombatTask runs the AI for the FireTower class. The tower implementing this task will scan for enemies + * in a straight line from the current position to a maxRange, and change the state of the tower. + */ +public class FireTowerCombatTask extends DefaultTask implements PriorityTask { + //constants + private static final int INTERVAL = 1; //time interval to scan for enemies in seconds + private static final short TARGET = PhysicsLayer.NPC; //the type of targets this tower will detect + //The constants are names of events that will be triggered in the state machine + private static final String IDLE = "startIdle"; + private static final String PREP_ATTACK = "startAttackPrep"; + private static final String ATTACK = "startAttack"; + + //Class attributes + private final int priority; + private final float maxRange; + + private Vector2 towerPosition = new Vector2(10, 10); + private final Vector2 maxRangePosition = new Vector2(); + private PhysicsEngine physics; + private GameTime timeSource; + private long endTime; + private final RaycastHit hit = new RaycastHit(); + + private enum STATE { + IDLE, PREP_ATTACK, ATTACK + } + private STATE towerState = STATE.IDLE; + + /** + * Starts the task running, triggers the initial 'IDLE' event + */ + public FireTowerCombatTask(int priority, float maxRange) { + this.priority = priority; + this.maxRange = maxRange; + physics = ServiceLocator.getPhysicsService().getPhysics(); + timeSource = ServiceLocator.getTimeSource(); + } + + @Override + public void start() { + super.start(); + // get the tower coordinates + this.towerPosition = owner.getEntity().getCenterPosition(); + this.maxRangePosition.set(towerPosition.x + maxRange, towerPosition.y); + //default to idle state + owner.getEntity().getEvents().trigger(IDLE); + + endTime = timeSource.getTime() + (INTERVAL * 500); + } + + @Override + public void update() { + if (timeSource.getTime() >= endTime) { + updateTowerState(); + endTime = timeSource.getTime() + (INTERVAL * 1000); + } + } + + public void updateTowerState() { + switch (towerState) { + case IDLE -> { + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(PREP_ATTACK); + towerState = STATE.PREP_ATTACK; + } + } + case PREP_ATTACK -> { + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(ATTACK); + towerState = STATE.ATTACK; + } else { + owner.getEntity().getEvents().trigger(IDLE); + towerState = STATE.IDLE; + } + } + case ATTACK -> { + if (!isTargetVisible()) { + owner.getEntity().getEvents().trigger(IDLE); + towerState = STATE.IDLE; + } else { + owner.getEntity().getEvents().trigger(ATTACK); + Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), + new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f, 2f)); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), + (float) (owner.getEntity().getPosition().y + 0.75)); + ServiceLocator.getEntityService().register(newProjectile); + } + } + } + } + + public void stop() { + super.stop(); + owner.getEntity().getEvents().trigger(IDLE); + } + + public int getPriority() { + return !isTargetVisible() ? 0 : priority; + } + + private int getActivePriority() { + return !isTargetVisible() ? 0 : priority; + } + + private int getInactivePriority() { + return isTargetVisible() ? priority : 0; + } + + public boolean isTargetVisible() { + return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); + } +} diff --git a/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java new file mode 100644 index 000000000..297476ec0 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java @@ -0,0 +1,42 @@ +package com.csse3200.game.components.tower; + +import com.badlogic.gdx.audio.Sound; +import com.csse3200.game.components.Component; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.services.ServiceLocator; + +public class FireTowerAnimationController extends Component{ + //Event name constants + private static final String IDLE = "startIdle"; + private static final String PREP_ATTACK = "startAttackPrep"; + private static final String ATTACK = "startAttack"; + + //animation name constants + private static final String IDLE_ANIM = "idle"; + private static final String PREP_ATTACK_ANIM = "prepAttack"; + private static final String ATTACK_ANIM = "attack"; + //here we can add the sounds for the implemented animations + + AnimationRenderComponent animator; + + @Override + public void create() { + super.create(); + animator = this.entity.getComponent(AnimationRenderComponent.class); + entity.getEvents().addListener(IDLE, this::animateIdle); + entity.getEvents().addListener(PREP_ATTACK, this::animatePrepAttack); + entity.getEvents().addListener(ATTACK, this::animateAttack); + } + + void animateIdle() { + animator.startAnimation(IDLE_ANIM); + } + + void animatePrepAttack() { + animator.startAnimation(PREP_ATTACK_ANIM); + } + + void animateAttack() { + animator.startAnimation(ATTACK_ANIM); + } +} diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index b0ade3ab2..c290e4212 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -1,5 +1,7 @@ package com.csse3200.game.entities.factories; +import com.csse3200.game.components.tasks.FireTowerCombatTask; +import com.csse3200.game.components.tower.FireTowerAnimationController; import com.csse3200.game.entities.configs.*; import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.TextureAtlas; @@ -32,6 +34,7 @@ public class TowerFactory { private static final int WEAPON_TOWER_MAX_RANGE = 40; private static final String WALL_IMAGE = "images/towers/wallTower.png"; private static final String TURRET_ATLAS = "images/towers/turret01.atlas"; + private static final String FIRE_TURRET_ATLAS = "images/towers/fire_tower_atlas.atlas"; private static final String IDLE_ANIM = "idle"; private static final float IDLE_SPEED = 0.3f; private static final String DEPLOY_ANIM = "deploy"; @@ -40,7 +43,13 @@ public class TowerFactory { private static final float STOW_SPEED = 0.2f; private static final String FIRE_ANIM = "firing"; private static final float FIRE_SPEED = 0.25f; - private static final int INCOME_INTERVAL = 300; + private static final String FIRE_TOWER_IDLE_ANIM = "idle"; + private static final float FIRE_TOWER_IDLE_SPEED = 0.3f; + private static final String FIRE_TOWER_PREP_ATTACK_ANIM = "prepAttack"; + private static final float FIRE_TOWER_PREP_ATTACK_SPEED = 0.2f; + private static final String FIRE_TOWER_ATTACK_ANIM = "attack"; + private static final float FIRE_TOWER_ATTACK_SPEED = 0.25f; + private static final int INCOME_INTERVAL = 300; private static final int INCOME_TASK_PRIORITY = 1; private static final baseTowerConfigs configs = @@ -119,9 +128,24 @@ public static Entity createWeaponTower() { public static Entity createFireTower() { Entity fireTower = createBaseTower(); FireTowerConfig config = configs.fireTower; + + AITaskComponent aiTaskComponent = new AITaskComponent() + .addTask(new FireTowerCombatTask(COMBAT_TASK_PRIORITY, WEAPON_TOWER_MAX_RANGE)); + + AnimationRenderComponent animator = + new AnimationRenderComponent( + ServiceLocator.getResourceService() + .getAsset(FIRE_TURRET_ATLAS, TextureAtlas.class)); + animator.addAnimation(FIRE_TOWER_IDLE_ANIM, FIRE_TOWER_IDLE_SPEED, Animation.PlayMode.LOOP); + animator.addAnimation(FIRE_TOWER_PREP_ATTACK_ANIM, FIRE_TOWER_PREP_ATTACK_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(FIRE_TOWER_ATTACK_ANIM, FIRE_TOWER_ATTACK_SPEED, Animation.PlayMode.LOOP); + fireTower .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) - .addComponent(new CostComponent(config.cost)); + .addComponent(new CostComponent(config.cost)) + .addComponent(aiTaskComponent) + .addComponent(animator) + .addComponent(new FireTowerAnimationController()); return fireTower; } From 4bbef11b0e2692b6742274125f7052f95f3502ae Mon Sep 17 00:00:00 2001 From: Shivam Date: Fri, 8 Sep 2023 01:20:12 +1000 Subject: [PATCH 14/72] Added files StunTowerCombatTask, StunTowerAnimationController, stun_tower.png, stun_tower.atlas and finished the base functionality for createStunTower method in TowerFactory. --- .../assets/images/towers/stun_tower.atlas | 118 ++++++++++++ .../core/assets/images/towers/stun_tower.png | Bin 0 -> 6954 bytes .../csse3200/game/areas/ForestGameArea.java | 178 +++++++++--------- .../components/tasks/StunTowerCombatTask.java | 109 +++++++++++ .../tower/StunTowerAnimationController.java | 35 ++++ .../game/entities/factories/TowerFactory.java | 32 +++- 6 files changed, 381 insertions(+), 91 deletions(-) create mode 100644 source/core/assets/images/towers/stun_tower.atlas create mode 100644 source/core/assets/images/towers/stun_tower.png create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java create mode 100644 source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java diff --git a/source/core/assets/images/towers/stun_tower.atlas b/source/core/assets/images/towers/stun_tower.atlas new file mode 100644 index 000000000..feeea736b --- /dev/null +++ b/source/core/assets/images/towers/stun_tower.atlas @@ -0,0 +1,118 @@ + +stun_tower.png +size: 1024, 64 +format: RGBA8888 +filter: Nearest, Nearest +repeat: none +attack + rotate: false + xy: 2, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 4 +attack + rotate: false + xy: 136, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 9 +attack + rotate: false + xy: 203, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 1 +attack + rotate: false + xy: 270, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 6 +attack + rotate: false + xy: 404, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 3 +attack + rotate: false + xy: 538, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 8 +attack + rotate: false + xy: 672, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 0 +attack + rotate: false + xy: 739, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 5 +attack + rotate: false + xy: 873, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 2 +attack + rotate: false + xy: 940, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 7 +idle + rotate: false + xy: 69, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 1 +idle + rotate: false + xy: 337, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 3 +idle + rotate: false + xy: 471, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 0 +idle + rotate: false + xy: 605, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 5 +idle + rotate: false + xy: 605, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 4 +idle + rotate: false + xy: 806, 2 + size: 65, 45 + orig: 65, 45 + offset: 0, 0 + index: 2 diff --git a/source/core/assets/images/towers/stun_tower.png b/source/core/assets/images/towers/stun_tower.png new file mode 100644 index 0000000000000000000000000000000000000000..0d5c0d4f6c69a165cf03552b2a3c8e24e284fac5 GIT binary patch literal 6954 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 fixedPositions = new ArrayList<>(); //Generating ArrayList - - fixedPositions.add(new GridPoint2(5, 8)); - fixedPositions.add(new GridPoint2(12, 4)); - fixedPositions.add(new GridPoint2(20, 10)); - fixedPositions.add(new GridPoint2(33, 17)); - - for (GridPoint2 fixedPos : fixedPositions) { - Entity tree = ObstacleFactory.createMountain(); - spawnEntityAt(tree, fixedPos, true, false); - } - } +// private void spawnBuilding1() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); +// +// for (int i = 0; i < NUM_BUILDINGS; i++) { +// GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); +// Entity building1 = ObstacleFactory.createBuilding1(); +// spawnEntityAt(building1, randomPos, true, false); +// } +// } +// private void spawnBuilding2() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); +// +// for (int i = 0; i < NUM_BUILDINGS; i++) { +// GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); +// Entity building2 = ObstacleFactory.createBuilding2(); +// spawnEntityAt(building2, randomPos, true, false); +// } +// } + +// private void spawnMountains() { +// ArrayList fixedPositions = new ArrayList<>(); //Generating ArrayList +// +// fixedPositions.add(new GridPoint2(5, 8)); +// fixedPositions.add(new GridPoint2(12, 4)); +// fixedPositions.add(new GridPoint2(20, 10)); +// fixedPositions.add(new GridPoint2(33, 17)); +// +// for (GridPoint2 fixedPos : fixedPositions) { +// Entity tree = ObstacleFactory.createMountain(); +// spawnEntityAt(tree, fixedPos, true, false); +// } +// } private Entity spawnPlayer() { Entity newPlayer = PlayerFactory.createPlayer(); @@ -324,34 +326,34 @@ private void spawnXenoGrunts() { } } - private Entity spawnGhostKing() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(0, 0); - GridPoint2 randomPos - = RandomUtils.random(minPos, maxPos); - // = new GridPoint2(26, 26); - Entity ghostKing = NPCFactory.createGhostKing(player); - spawnEntityAt(ghostKing, randomPos, true, true); - return ghostKing; - - } - - private Entity spawnBossKing2() { - GridPoint2 minPos = new GridPoint2(0, 0); - GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); - - for (int i = 0; i < NUM_BOSS; i++) { - int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate - int randomY = MathUtils.random(0, maxPos.y); - GridPoint2 randomPos = new GridPoint2(fixedX, randomY); - bossKing2 = BossKingFactory.createBossKing2(player); - spawnEntityAt(bossKing2, - randomPos, - true, - false); - } - return bossKing2; - } +// private Entity spawnGhostKing() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(0, 0); +// GridPoint2 randomPos +// = RandomUtils.random(minPos, maxPos); +// // = new GridPoint2(26, 26); +// Entity ghostKing = NPCFactory.createGhostKing(player); +// spawnEntityAt(ghostKing, randomPos, true, true); +// return ghostKing; +// +// } + +// private Entity spawnBossKing2() { +// GridPoint2 minPos = new GridPoint2(0, 0); +// GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); +// +// for (int i = 0; i < NUM_BOSS; i++) { +// int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate +// int randomY = MathUtils.random(0, maxPos.y); +// GridPoint2 randomPos = new GridPoint2(fixedX, randomY); +// bossKing2 = BossKingFactory.createBossKing2(player); +// spawnEntityAt(bossKing2, +// randomPos, +// true, +// false); +// } +// return bossKing2; +// } /** * Creates multiple projectiles that travel simultaneous. They all have same @@ -364,13 +366,13 @@ private Entity spawnBossKing2() { * @param speed The speed of the projectiles. * @param quantity The amount of projectiles to spawn. */ - private void spawnMultiProjectile(Vector2 position, Entity target, int direction, int space, Vector2 speed, int quantity) { - int half = quantity / 2; - for (int i = 0; i < quantity; i++) { - spawnProjectile(position, target, space * half, direction, speed); - --half; - } - } +// private void spawnMultiProjectile(Vector2 position, Entity target, int direction, int space, Vector2 speed, int quantity) { +// int half = quantity / 2; +// for (int i = 0; i < quantity; i++) { +// spawnProjectile(position, target, space * half, direction, speed); +// --half; +// } +// } /** * Returns projectile that can do an area of effect damage @@ -393,11 +395,13 @@ private void spawnWeaponTower() { for (int i = 0; i < NUM_WEAPON_TOWERS; i++) { GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); - Entity weaponTower = TowerFactory.createWeaponTower(); + //Entity weaponTower = TowerFactory.createWeaponTower(); Entity wallTower = TowerFactory.createWallTower(); Entity fireTower = TowerFactory.createFireTower(); - spawnEntityAt(weaponTower, randomPos, true, true); + Entity stunTower = TowerFactory.createStunTower(); + //spawnEntityAt(weaponTower, randomPos, true, true); spawnEntityAt(fireTower, randomPos, true, true); + spawnEntityAt(stunTower, randomPos, true, true); spawnEntityAt(wallTower, new GridPoint2(randomPos.x + 3, randomPos.y), true, true); } } @@ -440,16 +444,16 @@ public void dispose() { this.unloadAssets(); } - private void spawnScrap() { - 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 scrap = DropFactory.createScrapDrop(); - spawnEntityAt(scrap, randomPos, true, false); - } - } +// private void spawnScrap() { +// 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 scrap = DropFactory.createScrapDrop(); +// spawnEntityAt(scrap, randomPos, true, false); +// } +// } private void spawnIncome() { GridPoint2 minPos = new GridPoint2(0, 0); 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 new file mode 100644 index 000000000..1c2cc5863 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/StunTowerCombatTask.java @@ -0,0 +1,109 @@ +package com.csse3200.game.components.tasks; + +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.ProjectileFactory; +import com.csse3200.game.physics.PhysicsEngine; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.raycast.RaycastHit; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; + + +public class StunTowerCombatTask extends DefaultTask implements PriorityTask { + //constants + private static final int INTERVAL = 1; + private static final short TARGET = PhysicsLayer.NPC; + //Following constants are names of events that will be triggered in the state machine + public static final String IDLE = "startIdle"; + public static final String ATTACK = "startAttack"; + + //Following are the class constants + private final int priority; + private final float maxRange; + private Vector2 towerPosition = new Vector2(10, 10); + private final Vector2 maxRangePosition = new Vector2(); + private PhysicsEngine physics; + private GameTime timeSource; + private long endTime; + private final RaycastHit hit = new RaycastHit(); + + //enums for the state triggers + private enum STATE { + IDLE, ATTACK + } + private STATE towerState = STATE.IDLE; + + public StunTowerCombatTask(int priority, float maxRange) { + this.priority = priority; + this.maxRange = maxRange; + physics = ServiceLocator.getPhysicsService().getPhysics(); + timeSource = ServiceLocator.getTimeSource(); + } + + @Override + public void start() { + super.start(); + //get the tower coordinates + this.towerPosition = owner.getEntity().getCenterPosition(); + this.maxRangePosition.set(towerPosition.x + maxRange, towerPosition.y); + //set the default state to IDLE state + owner.getEntity().getEvents().trigger(IDLE); + + endTime = timeSource.getTime() + (INTERVAL * 5000); + } + + public void update() { + if (timeSource.getTime() >= endTime) { + updateTowerState(); + endTime = timeSource.getTime() + (INTERVAL * 1000); + } + } + + public void updateTowerState() { + switch (towerState) { + case IDLE -> { + if(isTargetVisible()) { + owner.getEntity().getEvents().trigger(ATTACK); + towerState = STATE.ATTACK; + } + } + case ATTACK -> { + if (!isTargetVisible()) { + owner.getEntity().getEvents().trigger(IDLE); + towerState = STATE.IDLE; + } else { + owner.getEntity().getEvents().trigger(ATTACK); + Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), + new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f, 2f)); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), + (float) (owner.getEntity().getPosition().y + 0.75)); + ServiceLocator.getEntityService().register(newProjectile); + } + } + } + } + + public void stop() { + super.stop(); + owner.getEntity().getEvents().trigger(IDLE); + } + + public int getPriority() { + return !isTargetVisible() ? 0 : priority; + } + + public int getActivePriority() { + return !isTargetVisible() ? 0 : priority; + } + + public int getInactivePriority() { + return isTargetVisible() ? priority : 0; + } + + public boolean isTargetVisible() { + return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); + } +} diff --git a/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java new file mode 100644 index 000000000..8ff908b35 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java @@ -0,0 +1,35 @@ +package com.csse3200.game.components.tower; + +import com.badlogic.gdx.audio.Sound; +import com.csse3200.game.components.Component; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.services.ServiceLocator; + +public class StunTowerAnimationController extends Component { + //Event name constants + private static final String IDLE = "startIdle"; + private static final String ATTACK = "startAttack"; + //animation name constants + private static final String IDLE_ANIM = "idle"; + private static final String ATTACK_ANIM = "attack"; + + //further sounds can be added for the tower attacks/movement + + AnimationRenderComponent animator; + + @Override + public void create() { + super.create(); + animator = this.entity.getComponent(AnimationRenderComponent.class); + entity.getEvents().addListener(IDLE, this::animateIdle); + entity.getEvents().addListener(ATTACK, this::animateAttack); + } + + void animateIdle() { + animator.startAnimation(IDLE_ANIM); + } + + void animateAttack() { + animator.startAnimation(ATTACK_ANIM); + } +} diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index c290e4212..90edc11af 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -1,7 +1,9 @@ package com.csse3200.game.entities.factories; import com.csse3200.game.components.tasks.FireTowerCombatTask; +import com.csse3200.game.components.tasks.StunTowerCombatTask; import com.csse3200.game.components.tower.FireTowerAnimationController; +import com.csse3200.game.components.tower.StunTowerAnimationController; import com.csse3200.game.entities.configs.*; import com.badlogic.gdx.graphics.g2d.Animation; import com.badlogic.gdx.graphics.g2d.TextureAtlas; @@ -22,6 +24,8 @@ import com.csse3200.game.rendering.TextureRenderComponent; import com.csse3200.game.services.ServiceLocator; +import java.util.ServiceConfigurationError; + /** * Factory to create a tower entity. * @@ -34,7 +38,8 @@ public class TowerFactory { private static final int WEAPON_TOWER_MAX_RANGE = 40; private static final String WALL_IMAGE = "images/towers/wallTower.png"; private static final String TURRET_ATLAS = "images/towers/turret01.atlas"; - private static final String FIRE_TURRET_ATLAS = "images/towers/fire_tower_atlas.atlas"; + private static final String FIRE_TOWER_ATLAS = "images/towers/fire_tower_atlas.atlas"; + private static final String STUN_TOWER_ATLAS = "images/towers/stun_tower.atlas"; private static final String IDLE_ANIM = "idle"; private static final float IDLE_SPEED = 0.3f; private static final String DEPLOY_ANIM = "deploy"; @@ -49,7 +54,11 @@ public class TowerFactory { private static final float FIRE_TOWER_PREP_ATTACK_SPEED = 0.2f; private static final String FIRE_TOWER_ATTACK_ANIM = "attack"; private static final float FIRE_TOWER_ATTACK_SPEED = 0.25f; - private static final int INCOME_INTERVAL = 300; + private static final String STUN_TOWER_IDLE_ANIM = "idle"; + private static final float STUN_TOWER_IDLE_SPEED = 0.33f; + private static final String STUN_TOWER_ATTACK_ANIM = "attack"; + private static final float STUN_TOWER_ATTACK_SPEED = 0.12f; + private static final int INCOME_INTERVAL = 300; private static final int INCOME_TASK_PRIORITY = 1; private static final baseTowerConfigs configs = @@ -135,7 +144,7 @@ public static Entity createFireTower() { AnimationRenderComponent animator = new AnimationRenderComponent( ServiceLocator.getResourceService() - .getAsset(FIRE_TURRET_ATLAS, TextureAtlas.class)); + .getAsset(FIRE_TOWER_ATLAS, TextureAtlas.class)); animator.addAnimation(FIRE_TOWER_IDLE_ANIM, FIRE_TOWER_IDLE_SPEED, Animation.PlayMode.LOOP); animator.addAnimation(FIRE_TOWER_PREP_ATTACK_ANIM, FIRE_TOWER_PREP_ATTACK_SPEED, Animation.PlayMode.NORMAL); animator.addAnimation(FIRE_TOWER_ATTACK_ANIM, FIRE_TOWER_ATTACK_SPEED, Animation.PlayMode.LOOP); @@ -147,6 +156,7 @@ public static Entity createFireTower() { .addComponent(animator) .addComponent(new FireTowerAnimationController()); + fireTower.setScale(3, 3); return fireTower; } @@ -154,10 +164,24 @@ public static Entity createStunTower() { Entity stunTower = createBaseTower(); StunTowerConfig config = configs.stunTower; + AITaskComponent aiTaskComponent = new AITaskComponent() + .addTask(new StunTowerCombatTask(COMBAT_TASK_PRIORITY, WEAPON_TOWER_MAX_RANGE)); + + AnimationRenderComponent animator = + new AnimationRenderComponent( + ServiceLocator.getResourceService() + .getAsset(STUN_TOWER_ATLAS, TextureAtlas.class)); + animator.addAnimation(STUN_TOWER_IDLE_ANIM, STUN_TOWER_IDLE_SPEED, Animation.PlayMode.LOOP); + animator.addAnimation(STUN_TOWER_ATTACK_ANIM, STUN_TOWER_ATTACK_SPEED, Animation.PlayMode.LOOP); + stunTower .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) - .addComponent((new CostComponent(config.cost))); + .addComponent((new CostComponent(config.cost))) + .addComponent(aiTaskComponent) + .addComponent(animator) + .addComponent(new StunTowerAnimationController()); + stunTower.setScale(1.5f, 1.5f); return stunTower; } From bfee9421af85f4a9f3e97d7788af39c66a51646e Mon Sep 17 00:00:00 2001 From: Mohamad Date: Fri, 8 Sep 2023 02:07:29 +1000 Subject: [PATCH 15/72] Added the png and atlas file for the DroidTower --- .../assets/images/towers/DroidTower.atlas | 251 ++++++++++++++++++ .../core/assets/images/towers/DroidTower.png | Bin 0 -> 11118 bytes 2 files changed, 251 insertions(+) create mode 100644 source/core/assets/images/towers/DroidTower.atlas create mode 100644 source/core/assets/images/towers/DroidTower.png diff --git a/source/core/assets/images/towers/DroidTower.atlas b/source/core/assets/images/towers/DroidTower.atlas new file mode 100644 index 000000000..47c4d1626 --- /dev/null +++ b/source/core/assets/images/towers/DroidTower.atlas @@ -0,0 +1,251 @@ + +DroidTower.png +size: 1024, 64 +format: RGBA8888 +filter: Nearest, Nearest +repeat: none +attackDown + rotate: false + xy: 72, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 4 +attackDown + rotate: false + xy: 212, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 1 +attackDown + rotate: false + xy: 457, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 3 +attackDown + rotate: false + xy: 597, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 0 +attackDown + rotate: false + xy: 737, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 2 +attackUp + rotate: false + xy: 247, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 3 +attackUp + rotate: false + xy: 422, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 0 +attackUp + rotate: false + xy: 422, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 4 +death + rotate: false + xy: 422, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 0 +idle + rotate: false + xy: 422, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 0 +attackUp + rotate: false + xy: 632, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 2 +attackUp + rotate: false + xy: 842, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 1 +death + rotate: false + xy: 2, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 1 +death + rotate: false + xy: 177, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 6 +death + rotate: false + xy: 387, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 8 +death + rotate: false + xy: 527, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 5 +death + rotate: false + xy: 702, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 7 +goDown + rotate: false + xy: 37, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 1 +death + rotate: false + xy: 37, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 3 +goUp + rotate: false + xy: 37, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 2 +goDown + rotate: false + xy: 282, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 3 +goUp + rotate: false + xy: 282, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 0 +goDown + rotate: false + xy: 562, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 5 +goDown + rotate: false + xy: 772, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 4 +goUp + rotate: false + xy: 142, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 1 +death + rotate: false + xy: 142, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 4 +goDown + rotate: false + xy: 142, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 2 +goUp + rotate: false + xy: 352, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 3 +death + rotate: false + xy: 352, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 2 +goDown + rotate: false + xy: 352, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 0 +walk + rotate: false + xy: 107, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 1 +walk + rotate: false + xy: 317, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 3 +walk + rotate: false + xy: 492, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 0 +walk + rotate: false + xy: 667, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 2 +walk + rotate: false + xy: 807, 2 + size: 33, 38 + orig: 33, 38 + offset: 0, 0 + index: 4 diff --git a/source/core/assets/images/towers/DroidTower.png b/source/core/assets/images/towers/DroidTower.png new file mode 100644 index 0000000000000000000000000000000000000000..6fab7ed2da337ae247805579a17a3a8fa260ea3c GIT binary patch literal 11118 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 H#fSd^VmRi+ literal 0 HcmV?d00001 From d7870fd9aa8ac9da4f904e9266502e81a9ee6fd4 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Fri, 8 Sep 2023 02:09:06 +1000 Subject: [PATCH 16/72] Added all the configurations for the DroidTower --- source/core/assets/configs/tower.json | 5 +++++ .../game/entities/configs/DroidTowerConfig.java | 10 ++++++++++ .../game/entities/configs/baseTowerConfigs.java | 1 + 3 files changed, 16 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/entities/configs/DroidTowerConfig.java diff --git a/source/core/assets/configs/tower.json b/source/core/assets/configs/tower.json index fe44c4d17..d8a86a549 100644 --- a/source/core/assets/configs/tower.json +++ b/source/core/assets/configs/tower.json @@ -18,5 +18,10 @@ "health": 10, "baseAttack": 5, "cost": 1 + }, + "DroidTower": { + "health": 10, + "baseAttack": 5, + "cost": 1 } } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/entities/configs/DroidTowerConfig.java b/source/core/src/main/com/csse3200/game/entities/configs/DroidTowerConfig.java new file mode 100644 index 000000000..18ce675d5 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/configs/DroidTowerConfig.java @@ -0,0 +1,10 @@ +package com.csse3200.game.entities.configs; + +/** + * Defines a basic set of properties stored in entities config files to be loaded by Entity Factories. + */ +public class DroidTowerConfig { + public int health = 1; + public int baseAttack = 0; + public int cost = 1; +} diff --git a/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java index d0c920d0c..e76f7f138 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/baseTowerConfigs.java @@ -8,4 +8,5 @@ public class baseTowerConfigs { public WallTowerConfig wall = new WallTowerConfig(); public IncomeTowerConfig income = new IncomeTowerConfig(); public TNTTowerConfigs TNTTower = new TNTTowerConfigs(); + public DroidTowerConfig DroidTower = new DroidTowerConfig(); } \ No newline at end of file From 1863d8063ee6b56043526d237f67637c0f5acc96 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Fri, 8 Sep 2023 02:10:04 +1000 Subject: [PATCH 17/72] Created the DroidTower entity and added some of the components to it --- .../game/entities/factories/TowerFactory.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index 93aa93794..452f53a61 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -38,7 +38,7 @@ public class TowerFactory { private static final int COMBAT_TASK_PRIORITY = 2; private static final int WEAPON_TOWER_MAX_RANGE = 40; private static final int TNT_TOWER_MAX_RANGE = 6; - private static final int TNT_TOWER_RANGE = 5; + private static final int TNT_TOWER_RANGE = 6; private static final int TNT_KNOCK_BACK_FORCE = 10; private static final String WALL_IMAGE = "images/towers/wallTower.png"; private static final String TURRET_ATLAS = "images/towers/turret01.atlas"; @@ -133,6 +133,27 @@ public static Entity createTNTTower() { return TNTTower; } + /** + * This robotic unit is programmed to detect mobs within its vicinity and fire projectiles at them. + * The droid has the capability to switch its aim from high to low positions, thereby providing a versatile attack strategy. + * When it detects a mob, the droid releases a projectile that inflicts both physical damage and a slow-down effect on the target. + * @return entity + */ + public static Entity createDroidTower() { + Entity DroidTower = createBaseTower(); + DroidTowerConfig config = configs.DroidTower; + + + DroidTower + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + .addComponent(new CostComponent(config.cost)) + .addComponent(new TNTAnimationController()); + + DroidTower.getComponent(AnimationRenderComponent.class).scaleEntity(); + + return DroidTower; + } + /** * Creates a weaponry tower that shoots at mobs - This will most likely need to be extended From 71e40d4bfe82145d3b9e321e4327ae675de884ce Mon Sep 17 00:00:00 2001 From: Mohamad Date: Fri, 8 Sep 2023 02:37:00 +1000 Subject: [PATCH 18/72] Created the DroidAnimationController which listens to events relevant to DroidTower's animations --- .../tower/DroidAnimationController.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java diff --git a/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java new file mode 100644 index 000000000..e1fb8f098 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java @@ -0,0 +1,86 @@ +package com.csse3200.game.components.tower; + +import com.csse3200.game.components.Component; +import com.csse3200.game.rendering.AnimationRenderComponent; + +/** + * This class listens to events relevant to DroidTower entity's state and plays the animation when one + * of the events is triggered. + */ +public class DroidAnimationController extends Component { + private AnimationRenderComponent animator; + + /** + * Creation call for a DroidAnimationController, fetches the animationRenderComponent that this controller will + * be attached to and registers all the event listeners required to trigger the animations and sounds. + */ + @Override + public void create() { + super.create(); + animator = this.entity.getComponent(AnimationRenderComponent.class); + entity.getEvents().addListener("walkStart", this::animateWalk); + entity.getEvents().addListener("idleStart", this::animateDefault); + entity.getEvents().addListener("goUpStart",this::animateGoUp); + entity.getEvents().addListener("goDownStart",this::animateGoDown); + entity.getEvents().addListener("attackUpStart",this::animateAttackUp); + entity.getEvents().addListener("attackDownStart",this::animateAttackDown); + entity.getEvents().addListener("deathStart",this::animateDeath); + + } + + /** + * Initiates the walking animation for the robot. + * This method should be invoked when the robot is moving but not in combat. + */ + void animateWalk() { + animator.startAnimation("walk"); + } + + /** + * Starts the animation sequence for switching aim from above. + * Use this method when the robot is preparing to target mobs after aiming from below. + */ + void animateGoUp() { + animator.startAnimation("goUp"); + } + + /** + * Activates the animation sequence for switching aim from below. + * Use this method when the robot is preparing to target mobs after aiming from above. + */ + void animateGoDown() { + animator.startAnimation("goDown"); + } + + /** + * Triggers the animation for firing projectiles from an elevated aim. + * Invoke this method when the robot engages with mobs and aiming above. + */ + void animateAttackUp() { + animator.startAnimation("attackUp"); + } + + /** + * Starts the animation sequence for firing projectiles from a lowered aim. + * Use this method when the robot engages with mobs and aiming below. + */ + void animateAttackDown() { + animator.startAnimation("attackDown"); + } + + /** + * Triggers the robot's death animation. + * This method should be invoked when the robot's health reaches zero. + */ + void animateDeath() { + animator.startAnimation("death"); + } + + + /** + * Triggers the "default" animation for the entity. + * This method should be invoked when the entity returns to its default state. + */ + void animateDefault() { animator.startAnimation("default");} + +} From 5d850165f8e4ad403e7cd5dc50d5cf6755c4bb21 Mon Sep 17 00:00:00 2001 From: gregchan550 Date: Fri, 8 Sep 2023 09:56:46 +1000 Subject: [PATCH 19/72] Added physics layer checker to applyAoeEffect in EffectsComponent so that aoe effect will be applied only to entites with target layer --- .../com/csse3200/game/areas/ForestGameArea.java | 2 +- .../csse3200/game/components/EffectsComponent.java | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 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 b8f318de2..67b2bf40c 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -159,7 +159,7 @@ public void create() { playMusic(); // Types of projectile - spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.PLAYER, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); + spawnEffectProjectile(new Vector2(0, 3), PhysicsLayer.PLAYER, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); // spawnProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f)); // spawnMultiProjectile(new Vector2(0, 10), player, towardsMobs, 20, new Vector2(2f, 2f), 7); spawnXenoGrunts(); 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 036f68592..c41a90d38 100644 --- a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java @@ -5,6 +5,7 @@ import com.csse3200.game.entities.factories.ProjectileFactory; import com.csse3200.game.physics.BodyUserData; import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.components.ColliderComponent; import com.csse3200.game.physics.components.HitboxComponent; import com.csse3200.game.services.ServiceLocator; @@ -67,12 +68,12 @@ private void onCollisionEnd(Fixture me, Fixture other) { switch (effect) { case FIREBALL -> { if (aoe) { - applyAoeEffect(ProjectileEffects.FIREBALL); + applyAoeEffect(ProjectileEffects.FIREBALL, other); } } case BURN -> { if (aoe) { - applyAoeEffect(ProjectileEffects.BURN); + applyAoeEffect(ProjectileEffects.BURN, other); } else { applySingleEffect(ProjectileEffects.BURN, otherCombatStats); } @@ -108,7 +109,7 @@ public void applySingleEffect(ProjectileEffects effect, CombatStatsComponent tar * 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 */ - public void applyAoeEffect(ProjectileEffects effect) { + public void applyAoeEffect(ProjectileEffects effect, Fixture other) { Entity hostEntity = getEntity(); CombatStatsComponent hostCombatStats = hostEntity.getComponent(CombatStatsComponent.class); @@ -121,6 +122,12 @@ public void applyAoeEffect(ProjectileEffects effect) { for (int i = 0; i < nearbyEntities.size; i++) { Entity targetEntity = nearbyEntities.get(i); + + if (!PhysicsLayer.contains(targetLayer, targetEntity.getComponent(HitboxComponent.class).getLayer())) { + // Doesn't match our target layer, ignore + return; + } + CombatStatsComponent targetCombatStats = targetEntity.getComponent(CombatStatsComponent.class); if (targetCombatStats != null) { switch (effect) { From e513a76fc3e14e2b1302c825f4a764c6054b477c Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Fri, 8 Sep 2023 10:47:48 +1000 Subject: [PATCH 20/72] Fixed the position of projectiles to be lower to match towers --- .../com/csse3200/game/components/tasks/TowerCombatTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 82cde5225..12ae949db 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -117,7 +117,7 @@ public void updateTowerState() { // * TEMPORARYYYYYYY // Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.75)); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); ServiceLocator.getEntityService().register(newProjectile); } } From 406027b8535664ea4031e626115f7f90b7f45e9e Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Fri, 8 Sep 2023 13:21:33 +1000 Subject: [PATCH 21/72] Averts divide by zero edge case if a tower's firerate is reduced to 0 shots per second. --- .../game/components/tasks/TowerCombatTask.java | 6 +++++- .../tower/TowerUpgraderComponentTest.java | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index b426f70ae..74614dc98 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -203,7 +203,11 @@ private boolean isTargetVisible() { private void changeFireRateInterval(int perMinute) { float oldFireSpeed = 1/fireRateInterval; float newFireSpeed = oldFireSpeed + perMinute/60f; - fireRateInterval = 1/newFireSpeed; + if (newFireSpeed == 0) { + fireRateInterval = 0; + } else { + fireRateInterval = 1 / newFireSpeed; + } } /** diff --git a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java index f9ab79901..2533eb7e1 100644 --- a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java @@ -62,4 +62,20 @@ void increaseFireRate() { verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.FIRERATE, 60); assertEquals(0.5, towerCombatTask.getFireRateInterval()); } + + @Test + void divideByZeroDefaultToZero() { + entity.addComponent(towerUpgraderComponent); + AITaskComponent aiTaskComponent = new AITaskComponent(); + ServiceLocator.registerPhysicsService(mock(PhysicsService.class)); + ServiceLocator.registerTimeSource(mock(GameTime.class)); + TowerCombatTask towerCombatTask = new TowerCombatTask(10, 10, 1); + aiTaskComponent.addTask(towerCombatTask); + entity.addComponent(aiTaskComponent); + towerCombatTask.start(); + entity.create(); + entity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.FIRERATE, -60); + verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.FIRERATE, -60); + assertEquals(0., towerCombatTask.getFireRateInterval()); + } } From bd6a871a625328495f9240df07d3aa48a6036bf4 Mon Sep 17 00:00:00 2001 From: MajorDzaster Date: Fri, 8 Sep 2023 13:37:24 +1000 Subject: [PATCH 22/72] TowerUpgraderComponent is a component of all tower entities, rather than just weapon towers. --- .../com/csse3200/game/entities/factories/TowerFactory.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index 366b0c0f1..f2ce8f2e9 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -117,8 +117,7 @@ public static Entity createWeaponTower() { .addComponent(new CostComponent(config.cost)) .addComponent(aiTaskComponent) .addComponent(animator) - .addComponent(new TowerAnimationController()) - .addComponent(new TowerUpgraderComponent()); + .addComponent(new TowerAnimationController()); return weapon; @@ -132,7 +131,8 @@ public static Entity createBaseTower() { Entity tower = new Entity() .addComponent(new ColliderComponent()) .addComponent(new HitboxComponent().setLayer(PhysicsLayer.OBSTACLE)) // TODO: we might have to change the names of the layers - .addComponent(new PhysicsComponent().setBodyType(BodyType.StaticBody)); + .addComponent(new PhysicsComponent().setBodyType(BodyType.StaticBody)) + .addComponent(new TowerUpgraderComponent()); return tower; } From 0afcd0cdcc292a5436805f3cd232a5204671e902 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Fri, 8 Sep 2023 13:58:55 +1000 Subject: [PATCH 23/72] Update TowerCombatTask.java updated to return without editing firerate if division by zero is attempted. --- .../com/csse3200/game/components/tasks/TowerCombatTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 74614dc98..1719b4712 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -204,7 +204,7 @@ private void changeFireRateInterval(int perMinute) { float oldFireSpeed = 1/fireRateInterval; float newFireSpeed = oldFireSpeed + perMinute/60f; if (newFireSpeed == 0) { - fireRateInterval = 0; + return; } else { fireRateInterval = 1 / newFireSpeed; } From 5549e1291979338e37138395e439abb73e94acc9 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Fri, 8 Sep 2023 14:02:20 +1000 Subject: [PATCH 24/72] Update TowerUpgraderComponentTest.java Fixed failing test after previous edit --- .../game/components/tower/TowerUpgraderComponentTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java index 2533eb7e1..703a1299b 100644 --- a/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/tower/TowerUpgraderComponentTest.java @@ -64,7 +64,7 @@ void increaseFireRate() { } @Test - void divideByZeroDefaultToZero() { + void divideByZeroDefaultToIgnore() { entity.addComponent(towerUpgraderComponent); AITaskComponent aiTaskComponent = new AITaskComponent(); ServiceLocator.registerPhysicsService(mock(PhysicsService.class)); @@ -76,6 +76,6 @@ void divideByZeroDefaultToZero() { entity.create(); entity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.FIRERATE, -60); verify(towerUpgraderComponent).upgradeTower(TowerUpgraderComponent.UPGRADE.FIRERATE, -60); - assertEquals(0., towerCombatTask.getFireRateInterval()); + assertEquals(1., towerCombatTask.getFireRateInterval()); } } From 4441a3b3fc5ba6fdd0de150cec75bf10be7fe1fa Mon Sep 17 00:00:00 2001 From: Shivam Date: Fri, 8 Sep 2023 14:24:27 +1000 Subject: [PATCH 25/72] Corrected animation lengths for fireTowers and stunTowers. --- .../images/towers/fire_tower_atlas.atlas | 92 ++++++++-------- .../assets/images/towers/fire_tower_atlas.png | Bin 8037 -> 6334 bytes .../assets/images/towers/stun_tower.atlas | 98 +++++++++--------- .../core/assets/images/towers/stun_tower.png | Bin 6954 -> 7075 bytes .../csse3200/game/areas/ForestGameArea.java | 11 +- .../components/tasks/FireTowerCombatTask.java | 4 +- .../components/tasks/StunTowerCombatTask.java | 4 +- .../game/entities/factories/TowerFactory.java | 5 +- 8 files changed, 109 insertions(+), 105 deletions(-) diff --git a/source/core/assets/images/towers/fire_tower_atlas.atlas b/source/core/assets/images/towers/fire_tower_atlas.atlas index 80fa90a9f..7c9ce2206 100644 --- a/source/core/assets/images/towers/fire_tower_atlas.atlas +++ b/source/core/assets/images/towers/fire_tower_atlas.atlas @@ -1,83 +1,83 @@ fire_tower_atlas.png -size: 2048, 256 +size: 1024, 64 format: RGBA8888 filter: Nearest, Nearest repeat: none -attac_prep - rotate: false - xy: 2, 2 - size: 159, 150 - orig: 159, 150 - offset: 0, 0 - index: 2 attack rotate: false - xy: 467, 2 - size: 150, 150 - orig: 150, 150 + xy: 122, 2 + size: 58, 58 + orig: 58, 58 offset: 0, 0 index: 1 attack rotate: false - xy: 923, 2 - size: 150, 150 - orig: 150, 150 + xy: 302, 2 + size: 58, 58 + orig: 58, 58 offset: 0, 0 index: 3 attack rotate: false - xy: 1075, 2 - size: 150, 150 - orig: 150, 150 + xy: 422, 2 + size: 58, 58 + orig: 58, 58 offset: 0, 0 index: 0 attack rotate: false - xy: 1379, 2 - size: 150, 150 - orig: 150, 150 + xy: 602, 2 + size: 58, 58 + orig: 58, 58 offset: 0, 0 index: 2 -attack_prep - rotate: false - xy: 315, 2 - size: 150, 150 - orig: 150, 150 - offset: 0, 0 - index: 0 idle rotate: false - xy: 163, 2 - size: 150, 150 - orig: 150, 150 - offset: 0, 0 - index: 1 -attack_prep - rotate: false - xy: 163, 2 - size: 150, 150 - orig: 150, 150 + xy: 62, 2 + size: 58, 58 + orig: 58, 58 offset: 0, 0 index: 1 idle rotate: false - xy: 619, 2 - size: 150, 150 - orig: 150, 150 + xy: 182, 2 + size: 58, 58 + orig: 58, 58 offset: 0, 0 index: 3 idle rotate: false - xy: 771, 2 - size: 150, 150 - orig: 150, 150 + xy: 362, 2 + size: 58, 58 + orig: 58, 58 offset: 0, 0 index: 0 idle rotate: false - xy: 1227, 2 - size: 150, 150 - orig: 150, 150 + xy: 542, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 2 +prep_attack + rotate: false + xy: 2, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 1 +prep_attack + rotate: false + xy: 242, 2 + size: 58, 58 + orig: 58, 58 + offset: 0, 0 + index: 0 +prep_attack + rotate: false + xy: 482, 2 + size: 58, 58 + orig: 58, 58 offset: 0, 0 index: 2 diff --git a/source/core/assets/images/towers/fire_tower_atlas.png b/source/core/assets/images/towers/fire_tower_atlas.png index cbdce58d1a7ef6309bca48b921303229e0648956..a8c5cc3ee2af7ab5d8115c7d0930244de40b6dcd 100644 GIT binary patch literal 6334 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 literal 8037 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 diff --git a/source/core/assets/images/towers/stun_tower.atlas b/source/core/assets/images/towers/stun_tower.atlas index feeea736b..f0033a197 100644 --- a/source/core/assets/images/towers/stun_tower.atlas +++ b/source/core/assets/images/towers/stun_tower.atlas @@ -7,112 +7,112 @@ repeat: none attack rotate: false xy: 2, 2 - size: 65, 45 - orig: 65, 45 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 4 attack rotate: false - xy: 136, 2 - size: 65, 45 - orig: 65, 45 + xy: 116, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 9 attack rotate: false - xy: 203, 2 - size: 65, 45 - orig: 65, 45 + xy: 173, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 1 attack rotate: false - xy: 270, 2 - size: 65, 45 - orig: 65, 45 + xy: 230, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 6 attack rotate: false - xy: 404, 2 - size: 65, 45 - orig: 65, 45 + xy: 344, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 3 attack rotate: false - xy: 538, 2 - size: 65, 45 - orig: 65, 45 + xy: 458, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 8 attack rotate: false - xy: 672, 2 - size: 65, 45 - orig: 65, 45 + xy: 572, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 0 attack rotate: false - xy: 739, 2 - size: 65, 45 - orig: 65, 45 + xy: 629, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 5 attack rotate: false - xy: 873, 2 - size: 65, 45 - orig: 65, 45 + xy: 743, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 2 attack rotate: false - xy: 940, 2 - size: 65, 45 - orig: 65, 45 + xy: 800, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 7 idle rotate: false - xy: 69, 2 - size: 65, 45 - orig: 65, 45 + xy: 59, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 1 idle rotate: false - xy: 337, 2 - size: 65, 45 - orig: 65, 45 + xy: 287, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 3 idle rotate: false - xy: 471, 2 - size: 65, 45 - orig: 65, 45 + xy: 401, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 0 idle rotate: false - xy: 605, 2 - size: 65, 45 - orig: 65, 45 + xy: 515, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 index: 5 idle rotate: false - xy: 605, 2 - size: 65, 45 - orig: 65, 45 + xy: 686, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 - index: 4 + index: 2 idle rotate: false - xy: 806, 2 - size: 65, 45 - orig: 65, 45 + xy: 857, 2 + size: 55, 45 + orig: 55, 45 offset: 0, 0 - index: 2 + index: 4 diff --git a/source/core/assets/images/towers/stun_tower.png b/source/core/assets/images/towers/stun_tower.png index 0d5c0d4f6c69a165cf03552b2a3c8e24e284fac5..025b35a4c77b083e3575c76a7ebd8547028f1531 100644 GIT binary patch literal 7075 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�e6j{}&rohLn+IZcC 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 literal 6954 zcmbVRc{tSF`yQfXvL!P@WQk&yCQG5ro2`_kRkAe9$UcceGJ|4lL!-Px+gMsK*0Rf3 z$5J%5ERkXC*%=Ij`Axmw<OdTXU}mya#*y8{H@0a>>{hYskgtxg6Rv=tXK{ zQlHPP{gY0WNmkJ`U#HU1OIjvYPj7`%i|~4QGrI$46@H|PnRGN!StFk-Y~^6{G*ZyL z=7U!{RjU;WQWagrv}Xo9;q%3jugvxv3Mqz*|1Xz=!k?1B8dw*9M7fr-|Cw!#fj(8| z|1_nm?AS5?>GBft1TOE(f4cBBJub-E!00EX%y-S*1H*=Jo+Vg2vJYSAzg^J1=vEl) zwct#^XO;CbLtJ#K`2oOSEf_%D@VtYTyCvqI9%AcT6)p;XPOjVv$$sAjfzl|SaCyvRuKbP;i>f^(@nlL z3O#~<6Px>uzs7qf;AAN|r2LAWsrhiw`oR>yfkGfr5Xboab1D5-XukXA=BLm=`sQs& zppQ2QFct{e0s;Z(LJWTCrsQIAg50Q9?fTE>G~vyTJ>=&5j&kfA7`x(qJ`%f#FTIW6 zH)z)V$P#pIEQ+J2_oQKSPA!E+9nydH~p__d%4 z(!bJg*{NA;pWG$|YNyb>+f~y5pUTm3Ybn~L|Z+1VYMFn!D zX97Q4IbMDOrgO+W>C24tg`e#G6(23PjTu2{VW|t%w~1F||Ml%-LCI75s>0{3&^%Sm zdm7WYl`qVaZ@OV?ESmrF+)3HBF7AE4;UdV0C58ZxGT#YPZ7jPyZZew8Lu{BR5pE31;y?yw5eH#`Y(0HbqYfs=SR0piZ_DW zM3R-;i>kcI^Q0>oq27WoQyk2@7j-e0H%Zb84QkLGJkWTmy07x2_bscu+dSS_$urC? zP+%Zt3&Qe~BucF8T#i(YXo|i9UUZTnc7_lGxknhR4{c+E>ZA@vJS}F4ulVSypM^#h z3{KA+hPgMU_~o5>zqW_-4-Cjejo zfyT(b!zn2Hy!~sR1rqur(+MDe@Ek^Q*r-KiBU$q5bl|;LnOmjF>m;f+;&g<;6KgPZ zEiMpVyT69(hA)vO|F8!foh1=s25%OOJ~%oDw7IsUyt$)^l7Rwd=b+m?bxicfqN3z2 zrmIGU(4_}h$h8gKwEqT8&+=M@Rx{X-YNMmag-Y2z2xjK^N*wk$B(QkInSJ_AfKgNQ zg_kj9&D&2Y(D7Fuc@+&h9h=@a2M2phLbcSQ+?PlLJ#TAzC-d{>kDx-T61ILa#>hKK zAU<@e5^ULLxaDP^JSmNo5%)B!zwhTok?;!^)_r%6_?w@A31x$TQfHsr`TIU|lpW9s zlIL0+=v-#KK0XCctxn6!1@3Wyv&WSCydGre5&j&ysspkI+xsh~{ySmIxp0_hb;&mpv zG>WH2)5lk$D0AVU%Bqh6@$`#Me1~7GWF3dWX0ID9^Fh}_ z4#}#JP)I9_X9dX;+EUhG=di6+-)<2JK4(?mQ%HC#1HYnV=Z|P8h;=JR1L!#|omnAv z@eR=4KXGdm8Efr?X>2fY=kGM#58~OBf7&v$uJYNh5wH2JIsn@Cv_d>J$txS?K2#%k zdqWg(Q>eM*DiphI-MQz`V-q7r$F{ndC?I}1WUCtm=iK;2ACa!pb%#uT zfA8~XB}Px@ya9VFG3o_SqvyW1^sw#Q*6`N1T8EC2&j&(*Cwp?`3G8;l zqj+8}PNEMY{bGh%xkGT306+QY%VInj+euc-W${eZ78xZM;^&~Cos|I323b%bI;TwK zPR<*sLjl8|uTH-5&pS4-i94^RuMju#`AgZ9kVH>jLyWdV+#GP!a2&03-_e0Z_7 zI5U^If9{l_j}JbOh2Nukl4Sr()+dIeM<3{C62$CgIkP$S>Aey$(TZ=*@t8bQnZY6QvLWzxRXzn~GPORqK((M$Sb0%c)6$ANVf!yVnoiYyivlgFkD> z?46zb)ZT01*fFz6GX`%c?dCzYLgFt+5i3f2TY|UEK5=(Qucv&&{=r<&^3em~M$ej0 zrh1&Z#X5D@GVa%|V~r-~oYO%BY?+L{e)Xfsxcz?ukUdY!wgs@iqOhR-So6Injgx#H z>3uKmQtEnCu2~P3O`7a7^ExJ!z9oEz+kqKaA9I(R)&v=cx4CJs+ z_6LGOdQZFXJd;xvom$7)hietk-8m#=EQLs7PvsK7lRG zJ}&iUJkQa<+3wSB&LkMAX6W4%*~pQU^yF?@O>c8Mjtr<*!~iK0$@aVR-VgNc77I{f z7oECTzVdcyT4K4{(p;#I?l)WH^d~a18-_K(`LetE{Ovg-9NlS@U z5x3&;M%^PH#27-!z*%+Y-9tA9k?Ub_=#4@3k;9GKKLqRld=yLkgmrm zI;By*qE+y4Flb|JOADdsSnU$Ay$~1H)$r?Zk4uNFtO4|W8(4TP@aIlb$>@(#f)z1s z0+?a4%$~9s9hjkRt8R;iZf(hEQp+5v!_t?LAIs>g@pI}4$<2+Cw(hl%5}$frQXjC+ zxo}P7#LpybF9-zO$VSWmcuiS(;m#wVoO5?wTCm$JF{B5z+V8RQ)mt>fqU8F032>?; zXv3FThqn=U6Z0*TV3Q~+gRFoL%+sVLE|Jusv!gPI5h9R0;%TWRf1o?9E&Zep&er@T zTwn_Gday@b=uCdS>g!P3sO!`1lekCZhn&X89`UPJofHpqoL7uF`5Gq#g^PxzMXyeviS$J{4KId+ZfEhtkX}u&!NCepK&)!I|7_jX+k_wm@65OZ^FFJ zU(>iF=~TFwuRb*rcoqcEa}eOk*7zgcb?L#*JBhIRE9CFq4vE~E1qc3lphE*eSGON+ zsfM%db1IDFSm93Cydzjeayd(t-Pgp(AOvRB4WU}FAPX1DdVRopPhm;Skn~w-L-zel zNm+gtcDgH9y7v}H(lr|o4PmeC6y2bB*Y2F)Y(!rARx~B%yk(5@J>$dT@N6@ z2*~5kCFBW)I=sG^ zO7_x`D#?a$x<`{yQl_X37@fVVG3t<7h1hCTiaOL#&*qGwZ8DX$c2A1h`$nJ@EUIhe zGWn&HWDvYF-!roP1>S#Mq=Cx$UT?aM?`c8T>6;!m#XV#lgS~hn;fe1u+-`=fYZT-! z;uvl+VEnh&xoF8kp&<71HfyLYzM$uq(9E$3>{4}2H*+!*uu+FV$#T4p0bp%7r&hRvh+>SyNksc+31WsP+z3QW@izyLlD5LFmIGK z8AH0bjnjAt6@%-;ce*T$)Q?pn%YSCl=_k&$f^{vL&XbFI(fztDJgael@3HhEY zrL`@8oKJ-*UyB78_~b4=i^bRdQ2Lpip0qN9@%r!{8*f-y;ux>XD>>Vj$S#wEN&L)v z_$6KA0bqp7i91AH$g-C#E)Kdjdrz-+eU6TmxkSu(?(3Q(mlExD-Q10=9kthPD-ykH zly6{~dSzc`u}K&0s;9vj^u+iL1uz=@1DEE=(4I!(9Hxr<8eJ_MsdssyBeN4}W-`iX zCi0gOXH~fJ-FLeO&2GBBfttkwbaGskOkJbtS{c}<7IDQG-vU{1rTcrMYxeLm1a`~z zf(0Q_vS=$2i`rkpc^fCB(b6YO6ny9s1LF1+V4ER?$seI-CsIM8tI8{gqMtJYIYm{D zZmL}+Hk9iHDgL%X5iy1gOujZ>v72&0<33Q(6gM34h*q6nR*FY`7cITGov$Ne0k+C@ z_p&*IHr=$ITMSc6Z)w=z=eTtS*I~k+gkcS>!Mssf%kuE?T4-h{4&4fHjtOazEtNlg z#o@wI=nFS5XIo}kMi}O8)EdR(O5x9!(u=iaQ&AT+d#`pi1pWOw_4;6Z%|}M543SWN ztWOZaHoCz#tYh#Y|Lb}w-ybc|KfaTE*GC_Z3v{|@y|5%+l{P16r1`XwnPZtY5hS`k zT>|BAfY;629oU`M_%Ox~Xb_1lnu0_23p}y26>!`2XF`Rw zLEgQWl-y|hnX=wl23K+HhqtWv5Wx?khT7L2J2|4`2oCN{u{pzmG6o3mM(FKMyFh$j zuIc}=@F|!YAkRQ@7c*QJN)AX;gX^W2iuV>%JQ$}$#d$bp$FyZ-B`1Mh@Ia*B-bQ|# z(e)bKNCAVdz<-e5w4wCf<&L}NwnfmX>WRso#BI)l%&8>@gjP4M7Qt*rBa1t5xUv;1 zo({x_N<@p`f4N(({at0zuIl>llK;bZl_@fy@~pjjCDN-+e_b@ z&t?yUIsKLg&H1qs(K$tQ!An6-^<}_X0&RTmz9C63YWl6;+LtMP?E_ZTgiUqUD{om5 z62V&k57_J?w-@jts~o?*JNfb72^)2MH(I(o)AA>K+NF$)TERS(v2^6qy=O*P>b~jr zUPK*_W>t~vo!@V~ygLEzoio$7GEmQ+4O$ylYe~rHC16tc9gZHDvdJ9Xnbp3KRW+$i z!2FAH4+TRvCb(aGmTKt+G$U37?WL!?{h&R=(*FtZv->E&YM1RR!rA~+bfBO4trU4} zACr3_SM=n`5@F&8=FD$^rof0h&wMdhwW>qIeGJw@T^#mxYZPeB+vAZ2pa6wM{@7{2 zdg;$gd$6nHB3DP4S@j=iZ+V&J(z%nedy8u^v}=X13%Qy{F;bxCrfEb(B*S^CzM_n` zj#i{24}uT*tZ!tDof=V5z%5<=7{o0o`0}S(%E&ivp7@VyBaKE`w*A%s3w{cnWZhx~@2LnAaF==^M@>A0X|)Q!+lzK<8=xhb7X- z`!>{gC2c;0rR4yyu^DJ4BuWLDsau~XwY%T7EjXHgH5F6olV7Wo=AZBn?Q}pnQn+(m z+UA#?uE^XbLdx44Qol-4|G=OfF8PYxKaXdt1TOb~wdR31|FpF1^Ba5A&!Z60@6jSD zQ=hplU+b3mx1&wY*k&G*&+mLsAbxlvJM*$7i1@W-oZ?Gqifh2t+TAv9W($oI^&H71 z-%GoAsdb4Qkr#G6z)v@D!M4IJ87>X;o)Jkyy`vHUWk9)d;g#6rqhVuA<2T#_%vc!s98n6O;frk zdA+!P($|v+Mp5rT1J)Ex*T|>Mp_Sp+4QP6(fKDZ{kZl1y)tje_J{=t^h7B{bFzX{# zTe~9c)XoTuXb#H~xckMNe2&x-AM^QEJ~_;pPT ze`!$CZ(hkMKPeeahgX=ly<~78Vjl;@6jXc{RNtgtzZRGiGOm48)z+)%kTasI1cYyI zV0S#QXAxjbW*en=EE45CV5Sf9En(MB%GlWzz1GK^yQ3Vms!uN3FU!AX=Ry?(c#Re( zq!Ud(+PJUm+Fhicy09Xx`Uj&e%;gN)&X}iLI?I-7({iZU5B1R1xN?tb{Uyx1GD z>W7;CHCbiWj^$h-TEkpl`(a9Fx@o-0O!d1X&WwDwt&$=+Fai52hxDn- z6j#Av;k`pBAfuS4qG;t_LC%n?E1`rqlstV{)q@0%C3QWKSc=|23$P{~{lryZ4XsI4 zH!tFos{K|>T7U2umx|m&2|Mg_xv(#hvw-ljE2v$WTK%-;3b>t)radJed^)`UQErRD z#_lTzG(Wk8<-McDyKR>iy)~KkY0VT%h$T|neeJI`?zSB2uzcx?+&+HFM;09$1%jFn z@tihlYW|ySZ9G#wuChYA8Od8HQTJWn!?#S>vW-aI1h;}+V*A$6a>`FPz0aZ|vcnul znRTp5-oD1UnVg0B3FOR?kjZ>f@Kx6G_tmj4=*@vPZ`UE&xvPaHH55uv%b;O!bWATg zV~!S|>~2Ria8p{~ekm4NC~KClPMmyeLEb94F9k+;I2XrPpX~8|7aZ>E%D@;~wnHt4 z($AO;lr?Yh_KWuCazVUBJ+D&Y4`z$<|KZ6WKhr3P6AzxBEMWyayqbU==^Vc8E)z4+-0Hro8l0cyTH zoeKi7w4Lz;pAjMVN5PRf-pPWYg95Q}*;~;vv`ooH+IacX{9fyTcWf&)NdJdZSuL1; zHM#@i2Wcn-=uwNi+{3>udXsTB#Hi<_Ax|*YAs(kzE6&KKl}R(DbtNt}80?lp?=rXe z>yTlO+NDTEPp7j;qn;bbRU_oUnf39-;sxwpK>$nN8PA6?95oc)qWuhNDQ?>yTP|*6 zCCDHGA)GN?_paZjB#CaPW?S}Ozq|U6{o Date: Fri, 8 Sep 2023 15:19:02 +1000 Subject: [PATCH 26/72] Added javadocs to StunTowerAnimationController, FireTowerAnimationController and mehtod in towerFactory classes. --- .../components/tasks/FireTowerCombatTask.java | 28 +++++++++++++++++ .../components/tasks/StunTowerCombatTask.java | 30 +++++++++++++++++++ .../tower/FireTowerAnimationController.java | 16 ++++++++++ .../tower/StunTowerAnimationController.java | 13 ++++++++ .../game/entities/factories/TowerFactory.java | 9 ++++++ 5 files changed, 96 insertions(+) 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 560790ce8..eff97273f 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 @@ -50,6 +50,9 @@ public FireTowerCombatTask(int priority, float maxRange) { timeSource = ServiceLocator.getTimeSource(); } + /** + * starts this task and triggers the IDLE animation + */ @Override public void start() { super.start(); @@ -62,6 +65,9 @@ public void start() { endTime = timeSource.getTime() + (INTERVAL * 500); } + /** + * this method is called everytime state of the tower needs to be changed. + */ @Override public void update() { if (timeSource.getTime() >= endTime) { @@ -70,6 +76,9 @@ public void update() { } } + /** + * finite state machine for the FireTower. Detects mobs in a straight line and changes the state of the tower. + */ public void updateTowerState() { switch (towerState) { case IDLE -> { @@ -103,23 +112,42 @@ public void updateTowerState() { } } + /** + * stops the current animation. + */ public void stop() { super.stop(); owner.getEntity().getEvents().trigger(IDLE); } + /** + * gets the priority for the current task. + * @return (int) active priority if target is visible and inactive priority otherwise + */ public int getPriority() { return !isTargetVisible() ? 0 : priority; } + /** + * not currently used. + * @return the priority for this task + */ private int getActivePriority() { return !isTargetVisible() ? 0 : priority; } + /** + * not currently used. + * @return + */ private int getInactivePriority() { return isTargetVisible() ? priority : 0; } + /** + * detects targets from the centre of the tower to maxRange in a straight line. + * @return true if mobs are present and false otherwise. + */ public boolean isTargetVisible() { return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); } 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 830add348..b4fb3d802 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 @@ -12,6 +12,10 @@ import com.csse3200.game.services.ServiceLocator; +/** + * The StunTowerCombatTask runs the AI for the StunTower class. The tower scans for mobs and targets in a straight line + * from its centre coordinate and executes the trigger phrases for animations depending on the current state of tower. + */ public class StunTowerCombatTask extends DefaultTask implements PriorityTask { //constants private static final int INTERVAL = 1; @@ -36,6 +40,10 @@ private enum STATE { } private STATE towerState = STATE.IDLE; + /** + * @param priority Task priority when targets are detected (0 when nothing is present) + * @param maxRange Maximum effective range of the StunTower. + */ public StunTowerCombatTask(int priority, float maxRange) { this.priority = priority; this.maxRange = maxRange; @@ -43,6 +51,9 @@ public StunTowerCombatTask(int priority, float maxRange) { timeSource = ServiceLocator.getTimeSource(); } + /** + * Starts the task running and starts the Idle animation + */ @Override public void start() { super.start(); @@ -55,6 +66,10 @@ public void start() { endTime = timeSource.getTime() + (INTERVAL * 5000); } + /** + * updates the current state of the tower based on the current state of the game. If enemies are detected, attack + * state is activated and otherwise idle state remains. + */ public void update() { if (timeSource.getTime() >= endTime) { updateTowerState(); @@ -62,6 +77,10 @@ public void update() { } } + /** + * This method acts is the state machine for StunTower. Relevant animations are triggered based on relevant state + * of the game. If enemies are detected, state of the tower is changed to attack state. + */ public void updateTowerState() { switch (towerState) { case IDLE -> { @@ -86,11 +105,18 @@ public void updateTowerState() { } } + /** + * stops the current animation and switches back the state of the tower to IDLE. + */ public void stop() { super.stop(); owner.getEntity().getEvents().trigger(IDLE); } + /** + * returns the current priority of the task + * @return (int) active priority if target is visible and inactive priority otherwise + */ public int getPriority() { return !isTargetVisible() ? 0 : priority; } @@ -103,6 +129,10 @@ public int getInactivePriority() { return isTargetVisible() ? priority : 0; } + /** + * Searches for enemies/mobs in a straight line from the centre of the tower to maxRange in a straight line. + * @return true if targets are detected, false otherwise + */ public boolean isTargetVisible() { return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); } diff --git a/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java index 297476ec0..358d7a3a6 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/tower/FireTowerAnimationController.java @@ -5,6 +5,10 @@ import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; +/** + * Listens for events relevant to a weapon tower state. + * Each event will have a trigger phrase and have a certain animation attached. + */ public class FireTowerAnimationController extends Component{ //Event name constants private static final String IDLE = "startIdle"; @@ -19,6 +23,9 @@ public class FireTowerAnimationController extends Component{ AnimationRenderComponent animator; + /** + * Create method for FireTowerAnimationController. + */ @Override public void create() { super.create(); @@ -28,14 +35,23 @@ public void create() { entity.getEvents().addListener(ATTACK, this::animateAttack); } + /** + * Starts the idle animation + */ void animateIdle() { animator.startAnimation(IDLE_ANIM); } + /** + * starts the prep_attack animation + */ void animatePrepAttack() { animator.startAnimation(PREP_ATTACK_ANIM); } + /** + * starts the attack animation + */ void animateAttack() { animator.startAnimation(ATTACK_ANIM); } diff --git a/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java index 8ff908b35..fa4868c4c 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/tower/StunTowerAnimationController.java @@ -5,6 +5,9 @@ import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; +/** + * Listens to triggers phrases and executes the required animations. + */ public class StunTowerAnimationController extends Component { //Event name constants private static final String IDLE = "startIdle"; @@ -17,6 +20,10 @@ public class StunTowerAnimationController extends Component { AnimationRenderComponent animator; + /** + * Creation method for StunTowerAnimationController, fetches the animationRenderComponent that this controller will + * be attached to and registers all the event listeners required to trigger the animations. + */ @Override public void create() { super.create(); @@ -25,10 +32,16 @@ public void create() { entity.getEvents().addListener(ATTACK, this::animateAttack); } + /** + * Starts the idle animation + */ void animateIdle() { animator.startAnimation(IDLE_ANIM); } + /** + * starts the attack animation + */ void animateAttack() { animator.startAnimation(ATTACK_ANIM); } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index d10c6c367..70b1f8b14 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -136,10 +136,15 @@ public static Entity createWeaponTower() { } + /** + * Creates the FireTower entity which shoots at mobs traversing in a straight line. + * @return FireTower entity with relevant components. + */ public static Entity createFireTower() { Entity fireTower = createBaseTower(); FireTowerConfig config = configs.fireTower; + //Component that handles triggering events and animations AITaskComponent aiTaskComponent = new AITaskComponent() .addTask(new FireTowerCombatTask(COMBAT_TASK_PRIORITY, WEAPON_TOWER_MAX_RANGE)); @@ -161,6 +166,10 @@ public static Entity createFireTower() { return fireTower; } + /** + * Creates the StunTower entity which shoots at mobs traversing in a straight line. + * @return StunTower entity with relevant components. + */ public static Entity createStunTower() { Entity stunTower = createBaseTower(); StunTowerConfig config = configs.stunTower; From 46da325e4ef565e95fea8d82770ef729c4cf6660 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Fri, 8 Sep 2023 15:58:24 +1000 Subject: [PATCH 27/72] Adjusted the TNT effect so that it only affects target on the same lane --- .../com/csse3200/game/components/tower/TNTDamageComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/components/tower/TNTDamageComponent.java b/source/core/src/main/com/csse3200/game/components/tower/TNTDamageComponent.java index c019b92fd..ee220433f 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/TNTDamageComponent.java +++ b/source/core/src/main/com/csse3200/game/components/tower/TNTDamageComponent.java @@ -78,7 +78,7 @@ private void applyTNTDamage() { Vector2 positionSource = entity.getPosition(); Vector2 positionOther = otherEntity.getPosition(); - if (positionSource.dst(positionOther) <= radius) { + if (positionSource.dst(positionOther) <= radius && positionSource.y -3 <= positionOther.y && positionSource.y +3 >= positionOther.y) { HitboxComponent sourceHitbox = entity.getComponent(HitboxComponent.class); HitboxComponent otherHitbox = otherEntity.getComponent(HitboxComponent.class); From 490cfdfe135e6909347f1807170bf679c973388f Mon Sep 17 00:00:00 2001 From: Mohamad Date: Fri, 8 Sep 2023 16:01:53 +1000 Subject: [PATCH 28/72] Added the combat task for Droid tower which triggers animations according to game events --- .../components/tasks/DroidCombatTask.java | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java diff --git a/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java new file mode 100644 index 000000000..8512c2221 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java @@ -0,0 +1,183 @@ +package com.csse3200.game.components.tasks; + +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.ProjectileFactory; +import com.csse3200.game.physics.PhysicsEngine; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.raycast.RaycastHit; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; + +/** + * The DroidCombatTask runs the AI for the DroidTower class. The tower will scan for targets in a straight line + * from its center point until a point at (x + maxRange, y), where x,y are the cooridinates of the tower's center + * position. This component should be added to an AiTaskComponent attached to the tower instance. + */ +public class DroidCombatTask extends DefaultTask implements PriorityTask { + // Constants + private static final int INTERVAL = 1; // time interval to scan for enemies in seconds + private static final short TARGET = PhysicsLayer.NPC; // The type of targets that the tower will detect + // the following four constants are the event names that will be triggered in the state machine + private static final String GO_UP = "goUpStart"; + private static final String GO_DOWN = "goDownStart"; + private static final String ATTACK_UP = "attackUpStart"; + private static final String ATTACK_DOWN = "attackDownStart"; + private static final String WALK = "walkStart"; + private static final String DEATH = "deathStart"; + private static final String IDLE = "idleStart"; + + + // class attributes + private final int priority; // The active priority this task will have + private final float maxRange; + private Vector2 towerPosition = new Vector2(10, 10); // initial placeholder value - will be overwritten + private final Vector2 maxRangePosition = new Vector2(); + private PhysicsEngine physics; + private GameTime timeSource; + private long endTime; + private final RaycastHit hit = new RaycastHit(); + + private enum STATE { + IDLE, UP, DOWN, SHOOT_UP, SHOOT_DOWN, WALK, DIE + } + private STATE towerState = STATE.WALK; + + /** + * @param priority Task priority when targets are detected (0 when nothing detected). Must be a positive integer. + * @param maxRange Maximum effective range of the weapon tower. This determines the detection distance of targets + */ + public DroidCombatTask(int priority, float maxRange) { + this.priority = priority; + this.maxRange = maxRange; + physics = ServiceLocator.getPhysicsService().getPhysics(); + timeSource = ServiceLocator.getTimeSource(); + } + + /** + * Starts the Task running, triggers the initial "idleStart" event. + */ + @Override + public void start() { + super.start(); + // Set the tower's coordinates + this.towerPosition = owner.getEntity().getCenterPosition(); + this.maxRangePosition.set(towerPosition.x + maxRange, towerPosition.y); + // Default to idle mode + owner.getEntity().getEvents().trigger(WALK); + + endTime = timeSource.getTime() + (INTERVAL * 500); + } + + /** + * The update method is what is run every time the TaskRunner in the AiTaskComponent calls update(). + * triggers events depending on the presence or otherwise of targets in the detection range + */ + @Override + public void update() { + if (timeSource.getTime() >= endTime) { + updateTowerState(); + endTime = timeSource.getTime() + (INTERVAL * 1000); + } + } + + /** + * Droid tower state machine. Updates tower state by scanning for mobs, and + * triggers the appropriate events corresponding to the STATE enum. + */ + public void updateTowerState() { + // configure tower state depending on target visibility + if (owner.getEntity().getComponent(CombatStatsComponent.class).getHealth() <= 0 && towerState != STATE.DIE) { + owner.getEntity().getEvents().trigger(DEATH); + towerState = STATE.DIE; + return; + } + switch (towerState) { + case WALK -> { + owner.getEntity().getEvents().trigger(WALK); + towerState = STATE.IDLE; + } + case IDLE -> { + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(ATTACK_UP); + towerState = STATE.DOWN; + } else { + owner.getEntity().getEvents().trigger(IDLE); + } + } + case SHOOT_DOWN -> { + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(ATTACK_DOWN); + towerState = STATE.UP; + } else { + owner.getEntity().getEvents().trigger(GO_UP); + towerState = STATE.UP; + } + } + case SHOOT_UP -> { + if (isTargetVisible()) { + + owner.getEntity().getEvents().trigger(ATTACK_UP); + towerState = STATE.DOWN; + } else { + owner.getEntity().getEvents().trigger(IDLE); + towerState = STATE.IDLE; + } + } + case DOWN -> { + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(GO_DOWN); + towerState = STATE.SHOOT_DOWN; + } else { + owner.getEntity().getEvents().trigger(IDLE); + towerState = STATE.IDLE; + } + } + case UP -> { + if (isTargetVisible()) { + owner.getEntity().getEvents().trigger(GO_UP); + towerState = STATE.SHOOT_UP; + } else { + owner.getEntity().getEvents().trigger(GO_UP); + towerState = STATE.IDLE; + + + } + } + case DIE -> { + if (owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) + owner.getEntity().setFlagForDelete(true); + } + } + } + /** + * For stopping the running task + */ + @Override + public void stop() { + super.stop(); +// owner.getEntity().getEvents().trigger(STOW); + } + + /** + * Returns the current priority of the task. + * @return active priority value if targets detected, inactive priority otherwise + */ + @Override + public int getPriority() { + return isTargetVisible() ? priority : 0; + } + + /** + * Uses a raycast to determine whether there are any targets in detection range + * @return true if a target is visible, false otherwise + */ + private boolean isTargetVisible() { + // If there is an obstacle in the path to the max range point, mobs visible. + return physics.raycast(towerPosition, maxRangePosition, TARGET, hit); + } +} From 8e804de640804176bbc79e013da3e443a3ce8ab6 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Fri, 8 Sep 2023 16:03:53 +1000 Subject: [PATCH 29/72] Added components to the Droid tower --- .../game/entities/factories/TowerFactory.java | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java index 452f53a61..8b0ea7579 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/TowerFactory.java @@ -1,7 +1,9 @@ package com.csse3200.game.entities.factories; import com.csse3200.game.components.TouchAttackComponent; +import com.csse3200.game.components.tasks.DroidCombatTask; import com.csse3200.game.components.tasks.TNTTowerCombatTask; +import com.csse3200.game.components.tower.DroidAnimationController; import com.csse3200.game.components.tower.TNTAnimationController; import com.csse3200.game.components.tower.TNTDamageComponent; import com.csse3200.game.entities.configs.*; @@ -43,7 +45,15 @@ public class TowerFactory { private static final String WALL_IMAGE = "images/towers/wallTower.png"; private static final String TURRET_ATLAS = "images/towers/turret01.atlas"; private static final String TNT_ATLAS = "images/towers/TNTTower.atlas"; + private static final String DROID_ATLAS = "images/towers/DroidTower.atlas"; + private static final float DROID_SPEED = 0.25f; private static final String DEFAULT_ANIM = "default"; + private static final String WALK_ANIM = "walk"; + private static final String DEATH_ANIM = "death"; + private static final String GO_UP = "goUp"; + private static final String GO_DOWN = "goDown"; + private static final String SHOOT_UP = "attackUp"; + private static final String SHOOT_DOWN = "attackDown"; private static final float DEFAULT_SPEED= 0.2f; private static final String DIG_ANIM = "dig"; private static final float DIG_SPEED = 0.2f; @@ -143,13 +153,30 @@ public static Entity createDroidTower() { Entity DroidTower = createBaseTower(); DroidTowerConfig config = configs.DroidTower; + AITaskComponent aiTaskComponent = new AITaskComponent() + .addTask(new DroidCombatTask(COMBAT_TASK_PRIORITY, WEAPON_TOWER_MAX_RANGE)); + + AnimationRenderComponent animator = + new AnimationRenderComponent( + ServiceLocator.getResourceService() + .getAsset(DROID_ATLAS, TextureAtlas.class)); + + animator.addAnimation(IDLE_ANIM, DROID_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(SHOOT_UP,DROID_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(SHOOT_DOWN,DROID_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(WALK_ANIM,DROID_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(DEATH_ANIM,DROID_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(GO_UP,DROID_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(GO_DOWN,DROID_SPEED, Animation.PlayMode.NORMAL); + + DroidTower .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) .addComponent(new CostComponent(config.cost)) - .addComponent(new TNTAnimationController()); - - DroidTower.getComponent(AnimationRenderComponent.class).scaleEntity(); + .addComponent(new DroidAnimationController()) + .addComponent(animator) + .addComponent(aiTaskComponent); return DroidTower; } From 7ca10b5aff423a6ac9c301c081a755177cd41cfa Mon Sep 17 00:00:00 2001 From: Mohamad Date: Fri, 8 Sep 2023 16:04:17 +1000 Subject: [PATCH 30/72] Spawned Droid Tower to the game --- .../csse3200/game/areas/ForestGameArea.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java index 4623cb16d..d6443a9b5 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -85,7 +85,8 @@ public class ForestGameArea extends GameArea { "images/iso_grass_3.png", "images/economy/scrap.png", "images/towers/mine_tower.png", - "images/towers/TNTTower.png" + "images/towers/TNTTower.png", + "images/towers/DroidTower.png" }; private static final String[] forestTextureAtlases = { "images/terrain_iso_grass.atlas", @@ -96,7 +97,8 @@ public class ForestGameArea extends GameArea { "images/mobs/xenoGruntRunning.atlas", "images/mobs/robot.atlas", "images/mobs/rangeBossRight.atlas", - "images/towers/TNTTower.atlas" + "images/towers/TNTTower.atlas", + "images/towers/DroidTower.atlas" }; private static final String[] forestSounds = { "sounds/Impact4.ogg", @@ -144,20 +146,21 @@ public void create() { playMusic(); // Types of projectile - spawnAoeProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f), 1); - spawnProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f)); - spawnMultiProjectile(new Vector2(0, 10), player, towardsMobs, 20, new Vector2(2f, 2f), 7); +// spawnAoeProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f), 1); +// spawnProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f)); +// spawnMultiProjectile(new Vector2(0, 10), player, towardsMobs, 20, new Vector2(2f, 2f), 7); spawnXenoGrunts(); spawnGhosts(); spawnWeaponTower(); - spawnIncome(); - spawnScrap(); +// spawnIncome(); +// spawnScrap(); bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); spawnTNTTower(); + spawnDroidTower(); playMusic(); } @@ -414,6 +417,18 @@ private void spawnTNTTower() { } + private void spawnDroidTower() { + GridPoint2 minPos = new GridPoint2(0, 0); + GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); + + for (int i = 0; i < NUM_WEAPON_TOWERS; i++) { + GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); + Entity weaponTower = TowerFactory.createDroidTower(); + spawnEntityAt(weaponTower, randomPos, true, true); + } + + } + private void playMusic() { Music music = ServiceLocator.getResourceService().getAsset(backgroundMusic, Music.class); From b9714cbe50eb739e8cb0a67c3da1c4be0ecfde4b Mon Sep 17 00:00:00 2001 From: freshc0w <121275444+freshc0w@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:13:46 +1000 Subject: [PATCH 31/72] Map boundaries --- .../csse3200/game/areas/ForestGameArea.java | 18 ++++++++++++------ .../csse3200/game/physics/PhysicsLayer.java | 2 ++ 2 files changed, 14 insertions(+), 6 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 67b2bf40c..32c3a32f8 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -159,7 +159,7 @@ public void create() { playMusic(); // Types of projectile - spawnEffectProjectile(new Vector2(0, 3), PhysicsLayer.PLAYER, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); + spawnEffectProjectile(new Vector2(5, 8), PhysicsLayer.PLAYER, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); // spawnProjectile(new Vector2(0, 10), player, towardsMobs, new Vector2(2f, 2f)); // spawnMultiProjectile(new Vector2(0, 10), player, towardsMobs, 20, new Vector2(2f, 2f), 7); spawnXenoGrunts(); @@ -195,23 +195,29 @@ private void spawnTerrain() { Vector2 worldBounds = new Vector2(tileBounds.x * tileSize, tileBounds.y * tileSize); // Left + // ! THIS ONE DOESNT WORK. GRIDPOINTS2UTIL.ZERO is (0, 4), not (0, 0) + // spawnEntityAt( + // ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), GridPoint2Utils.ZERO, false, false); spawnEntityAt( - ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), GridPoint2Utils.ZERO, false, false); + ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), new GridPoint2(1, 0), false, false); // Right spawnEntityAt( ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), - new GridPoint2(tileBounds.x, 0), + new GridPoint2(tileBounds.x -1, 0), false, false); // Top spawnEntityAt( - ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH), - new GridPoint2(0, tileBounds.y), + ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH * 7), + new GridPoint2(0, tileBounds.y - 1), false, false); // Bottom + // spawnEntityAt( + // ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH), GridPoint2Utils.ZERO, false, false); + // * TMPORARY spawnEntityAt( - ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH), GridPoint2Utils.ZERO, false, false); + ObstacleFactory.createWall(worldBounds.x, WALL_WIDTH * 7), new GridPoint2(0, 0), false, false); } private void spawnBuilding1() { GridPoint2 minPos = new GridPoint2(0, 0); diff --git a/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java b/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java index 637776166..98b656bdf 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsLayer.java @@ -9,6 +9,8 @@ public class PhysicsLayer { // NPC (Non-Playable Character) colliders public static final short NPC = (1 << 3); public static final short PROJECTILE = (1 << 4); + // * TEMPORARY WALL BOUNDARIES? + public static final short WALL = (1 << 5); public static final short ALL = ~0; public static boolean contains(short filterBits, short layer) { From 74f655bf0e839b201f36d710f8919f0d3427c412 Mon Sep 17 00:00:00 2001 From: freshc0w <121275444+freshc0w@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:16:25 +1000 Subject: [PATCH 32/72] Add delete on map boundary functionality --- .../components/DeleteOnMapEdgeComponent.java | 34 +++++++++++++++++++ .../game/components/RicochetComponent.java | 8 +++-- .../components/tasks/TowerCombatTask.java | 5 +-- .../com/csse3200/game/entities/Entity.java | 13 +++++-- .../entities/factories/ObstacleFactory.java | 2 +- .../entities/factories/ProjectileFactory.java | 16 +++++++-- .../csse3200/game/physics/PhysicsEngine.java | 2 +- 7 files changed, 68 insertions(+), 12 deletions(-) create mode 100644 source/core/src/main/com/csse3200/game/components/DeleteOnMapEdgeComponent.java diff --git a/source/core/src/main/com/csse3200/game/components/DeleteOnMapEdgeComponent.java b/source/core/src/main/com/csse3200/game/components/DeleteOnMapEdgeComponent.java new file mode 100644 index 000000000..4359c118d --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/DeleteOnMapEdgeComponent.java @@ -0,0 +1,34 @@ +package com.csse3200.game.components; + +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.Fixture; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.physics.BodyUserData; +import com.csse3200.game.physics.PhysicsLayer; + +/** + * Entities with this component will self destruct after hitting the grid edge + * upon collision. + */ +public class DeleteOnMapEdgeComponent extends Component { + + @Override + public void create() { + entity.getEvents().addListener("collisionEnd", this::onCollisionEnd); + } + + private void onCollisionEnd(Fixture me, Fixture other) { + Entity selfEntity = ((BodyUserData) me.getBody().getUserData()).entity; + + // * Should change the PhysicLayer to WALL / BOUNDARIES when established + if (!PhysicsLayer.contains(PhysicsLayer.WALL, other.getFilterData().categoryBits)) + return; + + Vector2 position = selfEntity.getPosition(); + + if (position.x <= 1 || position.x >= 18 || position.y < 0 || position.y >= 6.5) { + System.out.println("DELETION POSITION: " + position); + selfEntity.setFlagForDelete(true); + } + } +} diff --git a/source/core/src/main/com/csse3200/game/components/RicochetComponent.java b/source/core/src/main/com/csse3200/game/components/RicochetComponent.java index f2732375e..f0445abdc 100644 --- a/source/core/src/main/com/csse3200/game/components/RicochetComponent.java +++ b/source/core/src/main/com/csse3200/game/components/RicochetComponent.java @@ -21,9 +21,11 @@ public class RicochetComponent extends Component { private short targetLayer; private HitboxComponent hitBoxComponent; + private int bounceCount; - public RicochetComponent(short targetLayer) { + public RicochetComponent(short targetLayer, int bounceCount) { this.targetLayer = targetLayer; + this.bounceCount = bounceCount; } @Override @@ -42,10 +44,12 @@ private void onCollisionEnd(Fixture me, Fixture other) { if(hitBoxComponent.getFixture() != me) return; if(!PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits)) return; + + if(bounceCount >= 2) return; Entity projectile = ((BodyUserData) me.getBody().getUserData()).entity; - Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, projectile.getPosition().y + getRandomNumFrom(-250, 250)), new Vector2(2f, 2f)); + Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, projectile.getPosition().y + getRandomNumFrom(-250, 250)), new Vector2(2f, 2f), ++bounceCount); newProjectile.setPosition((float) (projectile.getPosition().x -1.5), (float) (projectile.getPosition().y)); // projectile.getComponent(AITaskComponent.class).addTask(new TrajectTask(new Vector2(100, projectile.getPosition().y + 50))); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 12ae949db..ff8c81aeb 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -112,10 +112,11 @@ public void updateTowerState() { } else { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state - Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.PLAYER, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + // Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + // Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); // * TEMPORARYYYYYYY - // Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f), 0); newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); ServiceLocator.getEntityService().register(newProjectile); diff --git a/source/core/src/main/com/csse3200/game/entities/Entity.java b/source/core/src/main/com/csse3200/game/entities/Entity.java index 676292dfb..b7b7f4ca3 100644 --- a/source/core/src/main/com/csse3200/game/entities/Entity.java +++ b/source/core/src/main/com/csse3200/game/entities/Entity.java @@ -249,9 +249,16 @@ public void update() { if (!enabled) { return; } - for (Component component : createdComponents) { - component.triggerUpdate(); - } + + // ! ITERATOR CAUSES PROBLEMS + // for (Component component : createdComponents) { + // component.triggerUpdate(); + // } + + for (int i = 0; i < createdComponents.size; i++) { + createdComponents.get(i).triggerUpdate(); + } + if (isFlaggedForDelete) { dispose(); return; diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ObstacleFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ObstacleFactory.java index c271ea2d6..0fef2bc2e 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ObstacleFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ObstacleFactory.java @@ -70,7 +70,7 @@ public static Entity createWall(float width, float height) { Entity wall = new Entity() .addComponent(new PhysicsComponent().setBodyType(BodyType.StaticBody)) // * TMEPORARRYY WALLL - .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE)); + .addComponent(new ColliderComponent().setLayer(PhysicsLayer.WALL)); wall.setScale(width, height); return wall; } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index 503df0b93..add9a0c29 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -9,6 +9,7 @@ import com.csse3200.game.components.tasks.TrajectTask; import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.components.DeleteOnMapEdgeComponent; import com.csse3200.game.components.MobProjectileAnimationController; import com.csse3200.game.entities.configs.BaseEntityConfig; import com.csse3200.game.entities.configs.NPCConfigs; @@ -83,9 +84,12 @@ public static Entity createPierceFireBall(short targetLayer, Vector2 destination * Create a ricochet fireball. * Ricochet fireball bounces off specified targets while applying intended effects i.e. damage */ - public static Entity createRicochetFireball(short targetLayer, Vector2 destination, Vector2 speed) { + public static Entity createRicochetFireball(short targetLayer, Vector2 destination, Vector2 speed, int bounceCount) { Entity fireBall = createFireBall(targetLayer, destination, speed); - fireBall.addComponent(new RicochetComponent(targetLayer)); + fireBall + .addComponent(new RicochetComponent(targetLayer, bounceCount)); + + setColliderSize(fireBall, (float) 0.4, (float) 0.15); return fireBall; } @@ -111,6 +115,8 @@ public static Entity createFireBall(short targetLayer, Vector2 destination, Vect projectile .addComponent(animator) .addComponent(new ProjectileAnimationController()); + // * TEMPORARY + // .addComponent(new DeleteOnMapEdgeComponent()); // .addComponent(new SelfDestructOnHitComponent(PhysicsLayer.OBSTACLE)); // projectile @@ -140,6 +146,8 @@ public static Entity createMobBall(short targetLayer, Vector2 destination, Vecto projectile .addComponent(animator) .addComponent(new MobProjectileAnimationController()); + // * TEMPORARY + // .addComponent(new DeleteOnMapEdgeComponent()); projectile .getComponent(AnimationRenderComponent.class).scaleEntity(); @@ -173,7 +181,9 @@ public static Entity createBaseProjectile(short targetLayer, Vector2 // specified target. // Original knockback value: 1.5f .addComponent(new TouchAttackComponent(targetLayer, 1.5f, true)) - .addComponent(new CombatStatsComponent(config.health, config.baseAttack)); + .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) + // *TEMPORARY + .addComponent(new DeleteOnMapEdgeComponent()); projectile .getComponent(PhysicsMovementComponent.class).setSpeed(speed); diff --git a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java index f4db690cb..d588834db 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java @@ -45,7 +45,7 @@ public PhysicsEngine(World world, GameTime timeSource) { public void update() { // Check for deleted bodies and joints -// checkAndDeleteBodies(); + // checkAndDeleteBodies(); // Updating physics isn't as easy as triggering an update every frame. Each frame could take a // different amount of time to run, but physics simulations are only stable if computed at a From 14c059178638711f53e3f54f211022b8d219c74d Mon Sep 17 00:00:00 2001 From: freshc0w <121275444+freshc0w@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:19:57 +1000 Subject: [PATCH 33/72] Fix the problem where mobs can't spawn through on the right --- .../core/src/main/com/csse3200/game/areas/ForestGameArea.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 32c3a32f8..8e49d3fc8 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -203,7 +203,7 @@ private void spawnTerrain() { // Right spawnEntityAt( ObstacleFactory.createWall(WALL_WIDTH, worldBounds.y), - new GridPoint2(tileBounds.x -1, 0), + new GridPoint2(tileBounds.x , 0), false, false); // Top From bc65a8df5f230346c30245effcb6960688015f06 Mon Sep 17 00:00:00 2001 From: gregchan550 Date: Fri, 8 Sep 2023 20:19:13 +1000 Subject: [PATCH 34/72] added handling for if targetCombatStats is null --- .../game/components/EffectsComponent.java | 52 +++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) 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 c41a90d38..915b2f1ac 100644 --- a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java @@ -15,6 +15,11 @@ import java.util.ArrayList; +/** + * This component applies an effect from the ProjectileEffects enum. This consists of fireball, burn, + * slow, and stun. Component also handles the targeting of specific layers and an area of effect + * application of effects. + */ public class EffectsComponent extends Component { private final float radius; private final ProjectileEffects effect; @@ -65,15 +70,16 @@ private void onCollisionEnd(Fixture me, Fixture other) { return; } + // Apply effect switch (effect) { case FIREBALL -> { if (aoe) { - applyAoeEffect(ProjectileEffects.FIREBALL, other); + applyAoeEffect(ProjectileEffects.FIREBALL); } } case BURN -> { if (aoe) { - applyAoeEffect(ProjectileEffects.BURN, other); + applyAoeEffect(ProjectileEffects.BURN); } else { applySingleEffect(ProjectileEffects.BURN, otherCombatStats); } @@ -96,6 +102,7 @@ public void applySingleEffect(ProjectileEffects effect, CombatStatsComponent tar return; } + // Apply effect switch (effect) { case FIREBALL -> {} case BURN -> { @@ -109,7 +116,7 @@ public void applySingleEffect(ProjectileEffects effect, CombatStatsComponent tar * 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 */ - public void applyAoeEffect(ProjectileEffects effect, Fixture other) { + public void applyAoeEffect(ProjectileEffects effect) { Entity hostEntity = getEntity(); CombatStatsComponent hostCombatStats = hostEntity.getComponent(CombatStatsComponent.class); @@ -120,6 +127,7 @@ public void applyAoeEffect(ProjectileEffects effect, Fixture other) { Array nearbyEntities = ServiceLocator.getEntityService().getNearbyEntities(hostEntity, radius); + // Iterate through nearby entities and apply effects for (int i = 0; i < nearbyEntities.size; i++) { Entity targetEntity = nearbyEntities.get(i); @@ -140,14 +148,26 @@ public void applyAoeEffect(ProjectileEffects effect, Fixture other) { case SLOW -> {} case STUN -> {} } + } else { + return; } } } + /** + * 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)) { @@ -173,4 +193,30 @@ public void run() { } }, delay, delay); } + +// private void slowEffect(Entity targetEntity) { +// // 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) { +// // Check if projectile is meant for towers or mobs +// if (!PhysicsLayer.contains(PhysicsLayer.HUMANS, targetEntity.getComponent(HitboxComponent.class).getLayer())) { +// // towers +// targetEntity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.FIRERATE, -30); +// } else if (!PhysicsLayer.contains(PhysicsLayer.NPC, targetEntity.getComponent(HitboxComponent.class).getLayer())) { +// // mobs +// } +// count++; +// } else { +// // Ensure to cancel the task when it's done +// this.cancel(); +// } +// } +// }, delay, delay); +// } } From 236ee129e60e047c141784a8267e2ef66bdc4137 Mon Sep 17 00:00:00 2001 From: gregchan550 Date: Fri, 8 Sep 2023 20:29:39 +1000 Subject: [PATCH 35/72] Added null checker for targetEntity's hitbox component in the applyAoeEffect method --- .../main/com/csse3200/game/components/EffectsComponent.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 915b2f1ac..a68bfff67 100644 --- a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java @@ -131,7 +131,9 @@ public void applyAoeEffect(ProjectileEffects effect) { for (int i = 0; i < nearbyEntities.size; i++) { Entity targetEntity = nearbyEntities.get(i); - if (!PhysicsLayer.contains(targetLayer, targetEntity.getComponent(HitboxComponent.class).getLayer())) { + HitboxComponent targetHitbox = targetEntity.getComponent(HitboxComponent.class); + if (targetHitbox == null) { return; } + if (!PhysicsLayer.contains(targetLayer, targetHitbox.getLayer())) { // Doesn't match our target layer, ignore return; } From e81b3320f15d55c517202dcd9c62c00c8753ec82 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Sat, 9 Sep 2023 02:18:57 +1000 Subject: [PATCH 36/72] fixed merge conflict --- .../csse3200/game/areas/ForestGameArea.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 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 ce5d0ea70..8e46f4513 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -181,10 +181,11 @@ public void create() { spawnDroidTower(); - playMusic(); + spawnEngineer(); bossKing1 = spawnBossKing1(); + bossKing2 = spawnBossKing2(); spawnTNTTower(); @@ -370,22 +371,22 @@ private void spawnXenoGrunts() { // // } -// private Entity spawnBossKing2() { -// GridPoint2 minPos = new GridPoint2(0, 0); -// GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); -// -// for (int i = 0; i < NUM_BOSS; i++) { -// int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate -// int randomY = MathUtils.random(0, maxPos.y); -// GridPoint2 randomPos = new GridPoint2(fixedX, randomY); -// bossKing2 = BossKingFactory.createBossKing2(player); -// spawnEntityAt(bossKing2, -// randomPos, -// true, -// false); -// } -// return bossKing2; -// } + private Entity spawnBossKing2() { + GridPoint2 minPos = new GridPoint2(0, 0); + GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); + + for (int i = 0; i < NUM_BOSS; i++) { + int fixedX = terrain.getMapBounds(0).x - 1; // Rightmost x-coordinate + int randomY = MathUtils.random(0, maxPos.y); + GridPoint2 randomPos = new GridPoint2(fixedX, randomY); + bossKing2 = BossKingFactory.createBossKing2(player); + spawnEntityAt(bossKing2, + randomPos, + true, + false); + } + return bossKing2; + } /** * Creates multiple projectiles that travel simultaneous. They all have same From 043d9d038fa49c48058bff9244024012ccd853f7 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Sat, 9 Sep 2023 11:12:47 +1000 Subject: [PATCH 37/72] Testing mob balls in game --- source/core/src/main/com/csse3200/game/areas/ForestGameArea.java | 1 + 1 file changed, 1 insertion(+) 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 422057ab1..bd215d6b5 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -161,6 +161,7 @@ public void create() { // Types of projectile spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.HUMANS, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); + spawnMobBall(new Vector2(15,10), PhysicsLayer.NPC, towardsTowers, new Vector2(2f, 2f)); spawnXenoGrunts(); spawnGhosts(); From 6e787bf72d6ccdefeebcf26ba5d7e6f6d4ebf1ae Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Sat, 9 Sep 2023 11:28:44 +1000 Subject: [PATCH 38/72] writing tests, minor changes to HWander and HAnimController --- .../csse3200/game/areas/ForestGameArea.java | 6 -- .../tasks/{ => human}/EngineerCombatTask.java | 2 +- .../tasks/human/HumanWanderTask.java | 2 - .../player/HumanAnimationControllerTest.java | 79 +++++++++++++++++-- .../factories/EngineerFactoryTest.java | 4 +- 5 files changed, 76 insertions(+), 17 deletions(-) rename source/core/src/main/com/csse3200/game/components/tasks/{ => human}/EngineerCombatTask.java (99%) 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 422057ab1..d2074a66e 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -509,11 +509,5 @@ private void spawnEngineer() { Entity engineer = EngineerFactory.createEngineer(); spawnEntityAt(engineer, new GridPoint2(1, i), true, true); } -// GridPoint2 minPos = new GridPoint2(0, 0); -// GridPoint2 maxPos = new GridPoint2(5, terrain.getMapBounds(0).sub(2, 2).y); -// GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); -// -// Entity engineer = EngineerFactory.createEngineer(); -// spawnEntityAt(engineer, randomPos, true, true); } } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java similarity index 99% rename from source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java rename to source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java index 40e91edea..1487fa714 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java @@ -1,4 +1,4 @@ -package com.csse3200.game.components.tasks; +package com.csse3200.game.components.tasks.human; import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.DefaultTask; diff --git a/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java index be2c570f7..a9b497130 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/HumanWanderTask.java @@ -5,8 +5,6 @@ import com.csse3200.game.ai.tasks.PriorityTask; import com.csse3200.game.ai.tasks.Task; import com.csse3200.game.components.CombatStatsComponent; -import com.csse3200.game.components.tasks.EngineerCombatTask; -import com.csse3200.game.entities.Entity; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.components.ColliderComponent; import com.csse3200.game.physics.components.HitboxComponent; diff --git a/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java b/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java index bf104bfbb..452d9441f 100644 --- a/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java +++ b/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java @@ -1,15 +1,63 @@ package com.csse3200.game.components.player; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.EngineerFactory; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.rendering.DebugRenderer; +import com.csse3200.game.rendering.RenderService; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +@ExtendWith(GameExtension.class) class HumanAnimationControllerTest { + private final String[] atlas = {"images/engineers/engineer.atlas"}; + private static final String[] sounds = { + "sounds/engineers/firing_auto.mp3", + "sounds/engineers/firing_single.mp3" + }; + + private final String[] animations = { + "idle_right", + "walk_left", + "walk_right", + "walk_prep", + "prep", + "firing_auto", + "firing_single", + "hit", + "death" + }; + + private Entity engineer; + + @Mock + GameTime gameTime; @BeforeEach void setUp() { + gameTime = mock(GameTime.class); + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + RenderService render = new RenderService(); + render.setDebug(mock(DebugRenderer.class)); + ServiceLocator.registerRenderService(render); + ResourceService resourceService = new ResourceService(); + ServiceLocator.registerResourceService(resourceService); + resourceService.loadTextureAtlases(atlas); + resourceService.loadSounds(sounds); + resourceService.loadAll(); + engineer = EngineerFactory.createEngineer(); } @AfterEach @@ -17,34 +65,53 @@ void tearDown() { } @Test - void create() { - } - - @Test - void animateIdleLeft() { + void shouldHaveAnimationController() { + assertNotNull(engineer.getComponent(HumanAnimationController.class), + "Created Engineer entity should have a HumanAnimationController"); } @Test - void animateIdleRight() { + void shouldAnimateIdleRight() { + engineer.getEvents().trigger("idleStart"); + when(gameTime.getDeltaTime()).thenReturn(0.1f); + engineer.update(); +// engineer.getComponent(AnimationRenderComponent.class).startAnimation("idle_right"); + assertEquals("idle_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'idleStart' event should trigger 'idle_right' animation'"); } @Test void animateLeftWalk() { + engineer.getEvents().trigger("walkLeftStart"); + assertEquals("walk_left", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'walkLeftStart' event should trigger 'walk_left' animation'"); } @Test void animateRightWalk() { + engineer.getEvents().trigger("walkRightStart"); + assertEquals("walk_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'walkRightStart' event should trigger 'walk_right' animation'"); } @Test void animateFiring() { + engineer.getEvents().trigger("firingSingleStart"); + assertEquals("firing_single", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'firingSingleStart' event should trigger 'firing_single' animation'"); } @Test void animateHit() { + engineer.getEvents().trigger("hitStart"); + assertEquals("hit", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'hitStart' event should trigger 'hit' animation'"); } @Test void animateDeath() { + engineer.getEvents().trigger("hitStart"); + assertEquals("death", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), + "'deathStart' event should trigger 'death' animation'"); } } \ No newline at end of file diff --git a/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java b/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java index 7be8641f4..3d80accb0 100644 --- a/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java +++ b/source/core/src/test/com/csse3200/game/entities/factories/EngineerFactoryTest.java @@ -39,13 +39,13 @@ @ExtendWith(GameExtension.class) class EngineerFactoryTest { - private String[] atlas = {"images/engineers/engineer.atlas"}; + private final String[] atlas = {"images/engineers/engineer.atlas"}; private static final String[] sounds = { "sounds/engineers/firing_auto.mp3", "sounds/engineers/firing_single.mp3" }; - private String[] animations = { + private final String[] animations = { "idle_right", "walk_left", "walk_right", From 52040235914c1b60a4ba8cce44105d509b899997 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Sat, 9 Sep 2023 12:00:23 +1000 Subject: [PATCH 39/72] fix mob balls not firing - update physical layer of scan target --- .../main/com/csse3200/game/components/tasks/MobAttackTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java b/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java index e6a83c610..0c5494c04 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java @@ -19,7 +19,7 @@ */ public class MobAttackTask extends DefaultTask implements PriorityTask { private static final int INTERVAL = 1; // time interval to scan for towers in - private static final short TARGET = PhysicsLayer.OBSTACLE; // mobs detecting for towers + private static final short TARGET = PhysicsLayer.HUMANS; // mobs detecting for towers // ^ fix this private static final String STOW = "stowStart"; From 786bae7670e1fe9267905bde28247491f9c00c5e Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha Date: Sat, 9 Sep 2023 12:07:23 +1000 Subject: [PATCH 40/72] commented out tests while debugging --- .../player/HumanAnimationControllerTest.java | 86 +++++++++---------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java b/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java index 452d9441f..99019845a 100644 --- a/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java +++ b/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java @@ -70,48 +70,46 @@ void shouldHaveAnimationController() { "Created Engineer entity should have a HumanAnimationController"); } - @Test - void shouldAnimateIdleRight() { - engineer.getEvents().trigger("idleStart"); - when(gameTime.getDeltaTime()).thenReturn(0.1f); - engineer.update(); -// engineer.getComponent(AnimationRenderComponent.class).startAnimation("idle_right"); - assertEquals("idle_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), - "'idleStart' event should trigger 'idle_right' animation'"); - } - - @Test - void animateLeftWalk() { - engineer.getEvents().trigger("walkLeftStart"); - assertEquals("walk_left", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), - "'walkLeftStart' event should trigger 'walk_left' animation'"); - } - - @Test - void animateRightWalk() { - engineer.getEvents().trigger("walkRightStart"); - assertEquals("walk_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), - "'walkRightStart' event should trigger 'walk_right' animation'"); - } - - @Test - void animateFiring() { - engineer.getEvents().trigger("firingSingleStart"); - assertEquals("firing_single", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), - "'firingSingleStart' event should trigger 'firing_single' animation'"); - } - - @Test - void animateHit() { - engineer.getEvents().trigger("hitStart"); - assertEquals("hit", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), - "'hitStart' event should trigger 'hit' animation'"); - } - - @Test - void animateDeath() { - engineer.getEvents().trigger("hitStart"); - assertEquals("death", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), - "'deathStart' event should trigger 'death' animation'"); - } +// @Test +// void shouldAnimateIdleRight() { +// engineer.getEvents().trigger("idleStart"); +// when(gameTime.getDeltaTime()).thenReturn(0.1f); +// assertEquals("idle_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), +// "'idleStart' event should trigger 'idle_right' animation'"); +// } +// +// @Test +// void animateLeftWalk() { +// engineer.getEvents().trigger("walkLeftStart"); +// assertEquals("walk_left", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), +// "'walkLeftStart' event should trigger 'walk_left' animation'"); +// } +// +// @Test +// void animateRightWalk() { +// engineer.getEvents().trigger("walkRightStart"); +// assertEquals("walk_right", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), +// "'walkRightStart' event should trigger 'walk_right' animation'"); +// } +// +// @Test +// void animateFiring() { +// engineer.getEvents().trigger("firingSingleStart"); +// assertEquals("firing_single", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), +// "'firingSingleStart' event should trigger 'firing_single' animation'"); +// } +// +// @Test +// void animateHit() { +// engineer.getEvents().trigger("hitStart"); +// assertEquals("hit", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), +// "'hitStart' event should trigger 'hit' animation'"); +// } +// +// @Test +// void animateDeath() { +// engineer.getEvents().trigger("hitStart"); +// assertEquals("death", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), +// "'deathStart' event should trigger 'death' animation'"); +// } } \ No newline at end of file From 714b446bbad3ba95435cb64460da6c00ac5eb313 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Sat, 9 Sep 2023 12:31:00 +1000 Subject: [PATCH 41/72] Changing proportion and spawning of mob balls --- .../com/csse3200/game/components/tasks/MobAttackTask.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java b/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java index 0c5494c04..f027205be 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/MobAttackTask.java @@ -127,8 +127,8 @@ public void updateMobState() { } else { owner.getEntity().getEvents().trigger(FIRING); Entity newProjectile = ProjectileFactory.createMobBall(PhysicsLayer.HUMANS, new Vector2(0, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - newProjectile.setPosition((float) (owner.getEntity().getPosition().x), (float) (owner.getEntity().getPosition().y)); - newProjectile.setScale(-1f, 0.5f); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x), (float) (owner.getEntity().getPosition().y + 0.1)); + newProjectile.setScale(-0.7f, 0.7f); ServiceLocator.getEntityService().register(newProjectile); mobState = STATE.STOW; owner.getEntity().getEvents().trigger("shootStart"); From 23d5db02992d8d34c572de81010ddcebbf48f1ef Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Sat, 9 Sep 2023 15:24:24 +1000 Subject: [PATCH 42/72] Added Engineer bullet creation --- .../projectiles/engineer_projectile.atlas | 41 ++++++++++++++++++ .../projectiles/engineer_projectile.png | Bin 0 -> 386 bytes .../images/projectiles/mobProjectile.atlas | 2 +- .../csse3200/game/areas/ForestGameArea.java | 3 +- .../projectile/EngineerBullets.java | 29 +++++++++++++ .../MobProjectileAnimationController.java | 3 +- .../game/components/tasks/TrajectTask.java | 1 + .../tasks/human/EngineerCombatTask.java | 2 +- .../entities/factories/ProjectileFactory.java | 27 +++++++++++- 9 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 source/core/assets/images/projectiles/engineer_projectile.atlas create mode 100644 source/core/assets/images/projectiles/engineer_projectile.png create mode 100644 source/core/src/main/com/csse3200/game/components/projectile/EngineerBullets.java rename source/core/src/main/com/csse3200/game/components/{ => projectile}/MobProjectileAnimationController.java (88%) diff --git a/source/core/assets/images/projectiles/engineer_projectile.atlas b/source/core/assets/images/projectiles/engineer_projectile.atlas new file mode 100644 index 000000000..47eb72f99 --- /dev/null +++ b/source/core/assets/images/projectiles/engineer_projectile.atlas @@ -0,0 +1,41 @@ + +engineer_projectile.png +size: 128, 32 +format: RGBA8888 +filter: Nearest, Nearest +repeat: none +bullet + rotate: false + xy: 68, 2 + size: 19, 19 + orig: 19, 19 + offset: 0, 0 + index: -1 +bullet + rotate: false + xy: 25, 2 + size: 20, 19 + orig: 20, 19 + offset: 0, 0 + index: -1 +default + rotate: false + xy: 25, 2 + size: 20, 19 + orig: 20, 19 + offset: 0, 0 + index: -1 +bullet + rotate: false + xy: 2, 2 + size: 21, 19 + orig: 21, 19 + offset: 0, 0 + index: -1 +bulletFinal + rotate: false + xy: 47, 2 + size: 19, 19 + orig: 19, 19 + offset: 0, 0 + index: -1 diff --git a/source/core/assets/images/projectiles/engineer_projectile.png b/source/core/assets/images/projectiles/engineer_projectile.png new file mode 100644 index 0000000000000000000000000000000000000000..a98ae11e2738244ed8bbd2b7a69d747efb26ae38 GIT binary patch literal 386 zcmV-|0e$|7P)%!e|5l00030!t2dRtB}ElS#zi|uf^^5JoR-ByY`whC}KOn)7HWlcjKf0Y@fdJ zknwN#lW<)=ggLL9YtkWz=TS0)>cv57z0b}=pdr-9i=+U$`tB#wJ? z-}yF&z|$`tuT>(0L9jt;tzM>~;s890-_^IPnlVZJ<^J#GbvOWe2)zAttetCa27_RO zw50)f7vI-6uAKg#&sVMY@b>GQYib6Apo=Pf`gis1D(@#&=yV5M6#emJpa1- z?kmqTTj+iK1}Gq2c*$SDwohMq$aww*9DrT8R*mA#-fUj}eC^t6&fw_TO=6<}00000 g00000002ki2U!--uDg4S4*&oF07*qoM6N<$f?lh)dH?_b literal 0 HcmV?d00001 diff --git a/source/core/assets/images/projectiles/mobProjectile.atlas b/source/core/assets/images/projectiles/mobProjectile.atlas index d069c0394..5dabfa025 100644 --- a/source/core/assets/images/projectiles/mobProjectile.atlas +++ b/source/core/assets/images/projectiles/mobProjectile.atlas @@ -20,7 +20,7 @@ rotate index: -1 default rotate: false - xy: 54, 2 + xy: 28, 2 size: 24, 23 orig: 24, 23 offset: 0, 0 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 7a586023b..5666d6af0 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -111,7 +111,8 @@ public class ForestGameArea extends GameArea { "images/mobs/rangeBossRight.atlas", "images/towers/TNTTower.atlas", "images/projectiles/basic_projectile.atlas", - "images/projectiles/mobProjectile.atlas" + "images/projectiles/mobProjectile.atlas", + "images/projectiles/engineer_bullets.atlas" }; private static final String[] forestSounds = { "sounds/Impact4.ogg", diff --git a/source/core/src/main/com/csse3200/game/components/projectile/EngineerBullets.java b/source/core/src/main/com/csse3200/game/components/projectile/EngineerBullets.java new file mode 100644 index 000000000..6b0a4d820 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/projectile/EngineerBullets.java @@ -0,0 +1,29 @@ +package com.csse3200.game.components.projectile; + +import com.csse3200.game.components.Component; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.services.ServiceLocator; //used for sound + +public class EngineerBullets extends Component{ + /** Event name constants */ + + AnimationRenderComponent animator; + + @Override + public void create() { + super.create(); + animator = this.entity.getComponent(AnimationRenderComponent.class); + entity.getEvents().addListener("start", this::animateStart); + entity.getEvents().addListener("final", this::animateFinal); + + } + + void animateStart() { + animator.startAnimation("bullet"); + } + + void animateFinal() { + animator.startAnimation("bulletFinal"); + } +} + diff --git a/source/core/src/main/com/csse3200/game/components/MobProjectileAnimationController.java b/source/core/src/main/com/csse3200/game/components/projectile/MobProjectileAnimationController.java similarity index 88% rename from source/core/src/main/com/csse3200/game/components/MobProjectileAnimationController.java rename to source/core/src/main/com/csse3200/game/components/projectile/MobProjectileAnimationController.java index e1a55775e..e72fe612e 100644 --- a/source/core/src/main/com/csse3200/game/components/MobProjectileAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/projectile/MobProjectileAnimationController.java @@ -1,5 +1,6 @@ -package com.csse3200.game.components; +package com.csse3200.game.components.projectile; +import com.csse3200.game.components.Component; import com.csse3200.game.rendering.AnimationRenderComponent; diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TrajectTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TrajectTask.java index 56c81912b..8ca977d2f 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TrajectTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TrajectTask.java @@ -41,6 +41,7 @@ public void start() { this.owner.getEntity().getEvents().trigger(START); this.owner.getEntity().getEvents().trigger("rotate"); + this.owner.getEntity().getEvents().trigger("start"); } public void switchProjectileState() { diff --git a/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java index 1487fa714..f26d84be0 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java @@ -131,7 +131,7 @@ public void updateEngineerState() { if (shotsFired <= 10) { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state - Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, + Entity newProjectile = ProjectileFactory.createEngineerBullet(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(4f, 4f)); newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index 58a705f0a..63482bd5f 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -9,7 +9,6 @@ import com.csse3200.game.components.tasks.TrajectTask; import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.components.CombatStatsComponent; -import com.csse3200.game.components.MobProjectileAnimationController; import com.csse3200.game.entities.configs.BaseEntityConfig; import com.csse3200.game.entities.configs.NPCConfigs; import com.csse3200.game.files.FileLoader; @@ -23,6 +22,7 @@ import com.csse3200.game.physics.components.PhysicsComponent; import com.csse3200.game.physics.components.PhysicsMovementComponent; import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.components.projectile.MobProjectileAnimationController; import com.csse3200.game.components.projectile.ProjectileAnimationController; /** @@ -115,6 +115,31 @@ public static Entity createFireBall(short targetLayer, Vector2 destination, Vect return projectile; } + /** + * Creates a enginner bullet + * + * @param targetLayer The enemy layer that the projectile collides with. + * @param destination The destination the projectile heads towards. + * @param speed The speed of the projectile. + * @return Returns a new fireball projectile entity. + */ + public static Entity createEngineerBullet(short targetLayer, Vector2 destination, Vector2 speed) { + Entity projectile = createBaseProjectile(targetLayer, destination, speed); + + AnimationRenderComponent animator = + new AnimationRenderComponent( + ServiceLocator.getResourceService() + .getAsset(BASE_PROJECTILE_ATLAS, TextureAtlas.class)); + animator.addAnimation("bullet", START_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation("bulletFinal", FINAL_SPEED, Animation.PlayMode.NORMAL); + + projectile + .addComponent(animator) + .addComponent(new ProjectileAnimationController()); + // .addComponent(new SelfDestructOnHitComponent(PhysicsLayer.OBSTACLE)); + + return projectile; + } /** * Creates a projectile specifically for mobs to shoot From 3f5b64485dd2eed89d7fc3a52dac69ad4d1231ca Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Sat, 9 Sep 2023 16:21:46 +1000 Subject: [PATCH 43/72] Added new Engineer Bullets for Engineers to shoot --- .../projectiles/engineer_projectile.atlas | 26 +++++++------------ .../csse3200/game/areas/ForestGameArea.java | 7 ++--- ... EngineerBulletsAnimationControlller.java} | 6 ++--- .../tasks/human/EngineerCombatTask.java | 3 ++- .../entities/factories/ProjectileFactory.java | 5 ++-- 5 files changed, 22 insertions(+), 25 deletions(-) rename source/core/src/main/com/csse3200/game/components/projectile/{EngineerBullets.java => EngineerBulletsAnimationControlller.java} (72%) diff --git a/source/core/assets/images/projectiles/engineer_projectile.atlas b/source/core/assets/images/projectiles/engineer_projectile.atlas index 47eb72f99..9790d718e 100644 --- a/source/core/assets/images/projectiles/engineer_projectile.atlas +++ b/source/core/assets/images/projectiles/engineer_projectile.atlas @@ -6,25 +6,11 @@ filter: Nearest, Nearest repeat: none bullet rotate: false - xy: 68, 2 + xy: 47, 2 size: 19, 19 orig: 19, 19 offset: 0, 0 index: -1 -bullet - rotate: false - xy: 25, 2 - size: 20, 19 - orig: 20, 19 - offset: 0, 0 - index: -1 -default - rotate: false - xy: 25, 2 - size: 20, 19 - orig: 20, 19 - offset: 0, 0 - index: -1 bullet rotate: false xy: 2, 2 @@ -34,8 +20,16 @@ bullet index: -1 bulletFinal rotate: false - xy: 47, 2 + xy: 68, 2 size: 19, 19 orig: 19, 19 offset: 0, 0 index: -1 +bullet + rotate: false + xy: 25, 2 + size: 20, 19 + orig: 20, 19 + offset: 0, 0 + index: -1 + 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 5666d6af0..a16d7d231 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -112,7 +112,7 @@ public class ForestGameArea extends GameArea { "images/towers/TNTTower.atlas", "images/projectiles/basic_projectile.atlas", "images/projectiles/mobProjectile.atlas", - "images/projectiles/engineer_bullets.atlas" + "images/projectiles/engineer_projectile.atlas" }; private static final String[] forestSounds = { "sounds/Impact4.ogg", @@ -162,7 +162,8 @@ public void create() { // Types of projectile spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.HUMANS, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); - spawnMobBall(new Vector2(15,10), PhysicsLayer.NPC, towardsTowers, new Vector2(2f, 2f)); + spawnMobBall(new Vector2(0, 8), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); + spawnXenoGrunts(); spawnGhosts(); @@ -300,7 +301,7 @@ private void spawnProjectile(Vector2 position, short targetLayer, int direction, * */ private void spawnMobBall(Vector2 position, short targetLayer, int direction, Vector2 speed) { - Entity Projectile = ProjectileFactory.createMobBall(targetLayer, new Vector2(direction, position.y), speed); + Entity Projectile = ProjectileFactory.createEngineerBullet(targetLayer, new Vector2(direction, position.y), speed); Projectile.setPosition(position); spawnEntity(Projectile); } diff --git a/source/core/src/main/com/csse3200/game/components/projectile/EngineerBullets.java b/source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationControlller.java similarity index 72% rename from source/core/src/main/com/csse3200/game/components/projectile/EngineerBullets.java rename to source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationControlller.java index 6b0a4d820..ad8d0fd57 100644 --- a/source/core/src/main/com/csse3200/game/components/projectile/EngineerBullets.java +++ b/source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationControlller.java @@ -4,7 +4,7 @@ import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; //used for sound -public class EngineerBullets extends Component{ +public class EngineerBulletsAnimationControlller extends Component{ /** Event name constants */ AnimationRenderComponent animator; @@ -13,8 +13,8 @@ public class EngineerBullets extends Component{ public void create() { super.create(); animator = this.entity.getComponent(AnimationRenderComponent.class); - entity.getEvents().addListener("start", this::animateStart); - entity.getEvents().addListener("final", this::animateFinal); + entity.getEvents().addListener("startProjectile", this::animateStart); + entity.getEvents().addListener("startProjectileFinal", this::animateFinal); } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java index f26d84be0..23dbea9c0 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/human/EngineerCombatTask.java @@ -134,7 +134,8 @@ public void updateEngineerState() { Entity newProjectile = ProjectileFactory.createEngineerBullet(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(4f, 4f)); - newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); + newProjectile.setScale(0.8f, 0.8f); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.3), (float) (owner.getEntity().getPosition().y + 0.15)); ServiceLocator.getEntityService().register(newProjectile); shotsFired += 1; reloadTime = timeSource.getTime(); diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index 63482bd5f..a99cf2826 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -22,6 +22,7 @@ import com.csse3200.game.physics.components.PhysicsComponent; import com.csse3200.game.physics.components.PhysicsMovementComponent; import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.components.projectile.EngineerBulletsAnimationControlller; import com.csse3200.game.components.projectile.MobProjectileAnimationController; import com.csse3200.game.components.projectile.ProjectileAnimationController; @@ -129,13 +130,13 @@ public static Entity createEngineerBullet(short targetLayer, Vector2 destination AnimationRenderComponent animator = new AnimationRenderComponent( ServiceLocator.getResourceService() - .getAsset(BASE_PROJECTILE_ATLAS, TextureAtlas.class)); + .getAsset("images/projectiles/engineer_projectile.atlas", TextureAtlas.class)); animator.addAnimation("bullet", START_SPEED, Animation.PlayMode.NORMAL); animator.addAnimation("bulletFinal", FINAL_SPEED, Animation.PlayMode.NORMAL); projectile .addComponent(animator) - .addComponent(new ProjectileAnimationController()); + .addComponent(new EngineerBulletsAnimationControlller()); // .addComponent(new SelfDestructOnHitComponent(PhysicsLayer.OBSTACLE)); return projectile; From a1a42bd3cd124277b6820ca326ed3b0e8441e428 Mon Sep 17 00:00:00 2001 From: cindyle1 Date: Sat, 9 Sep 2023 16:23:29 +1000 Subject: [PATCH 44/72] Added animation controller for the mob king --- .../images/projectiles/basic_projectile.atlas | 37 ++++++----- .../images/projectiles/basic_projectile.png | Bin 961 -> 1014 bytes .../projectiles/mobKing_projectile.atlas | 62 ++++++++++++++++++ .../images/projectiles/mobKing_projectile.png | Bin 0 -> 1351 bytes .../MobKingProjectAnimController.java | 32 +++++++++ .../game/components/tasks/TrajectTask.java | 2 - .../entities/factories/ProjectileFactory.java | 21 ++++++ 7 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 source/core/assets/images/projectiles/mobKing_projectile.atlas create mode 100644 source/core/assets/images/projectiles/mobKing_projectile.png create mode 100644 source/core/src/main/com/csse3200/game/components/projectile/MobKingProjectAnimController.java diff --git a/source/core/assets/images/projectiles/basic_projectile.atlas b/source/core/assets/images/projectiles/basic_projectile.atlas index 2f09b83bf..9d295b888 100644 --- a/source/core/assets/images/projectiles/basic_projectile.atlas +++ b/source/core/assets/images/projectiles/basic_projectile.atlas @@ -4,31 +4,38 @@ size: 256, 32 format: RGBA8888 filter: Nearest, Nearest repeat: none -projectile +projectileFinal rotate: false xy: 2, 2 size: 36, 19 orig: 36, 19 offset: 0, 0 - index: 4 -projectileFinal - rotate: false - xy: 103, 3 - size: 30, 18 - orig: 30, 18 - offset: 0, 0 - index: 3 + index: 2 projectile rotate: false xy: 40, 2 - size: 30, 19 - orig: 30, 19 + size: 36, 19 + orig: 36, 19 offset: 0, 0 - index: 2 + index: 4 projectile rotate: false - xy: 72, 2 - size: 29, 19 - orig: 29, 19 + xy: 78, 2 + size: 36, 19 + orig: 36, 19 offset: 0, 0 index: 1 +projectile + rotate: false + xy: 116, 2 + size: 36, 19 + orig: 36, 19 + offset: 0, 0 + index: 3 +default + rotate: false + xy: 116, 2 + size: 36, 19 + orig: 36, 19 + offset: 0, 0 + index: 3 diff --git a/source/core/assets/images/projectiles/basic_projectile.png b/source/core/assets/images/projectiles/basic_projectile.png index abf6cfc59d0865975f238b941b8c25e3734cbeaf..5fa3bb42cc8801c6673c71564653bb8b9889285a 100644 GIT binary patch delta 973 zcmV;;12X)<2lfY$B!9h0L_t(|UhSIAOH^SL#?Lx}BuHzr9LL7WMS{#iDr)*71VOT> zMTDE&1a6{51Z`Wi2--!fp#Fne1ns0&EfT~E=|jay`y$GPm}9p&cjjEroO|y$j*ec( z=LZiucV?XTzUMs8eeXLG0RR9100000000000000)f@kqIdVkfCpf{(Q=*vJO%{)lb z!e9se`*4(;Uukk?bIRsr;rFthw;ZIww*54eO48`z7W#b5ug!>^gCGIm!!}8nRs%8d zyE8GGxEiDRM+sVZnIdN*oezM5`C0N>){6tJG}zwaJ(d8lV`Pn;POR#fHv)DJ$`RPa zNE1w)jML=VJ%7IDu;SOPM$;%$JAbcjapoTHcASwDd1ccgntjqrQg}S9$%{LD<{#~exzAM-IiDrutDjY+^#mIT-4ymJ&keu^ zo9V0b8b*v2q)MAc8$_1u^4W8@x6$;C#j80>H>v)7YjU+x2$T+jslNPp%N=tNZ0z}a z4Xz{vbbm1Mt&Ds8R$Wy?K+Z#JEWH?XVe@Cj&r3a5GA|&)*haBedEF4!KvtDDjS7Gw zgUjc7hL&4^;@g05Ek!mT0xAf~9&-?`xpT@sO}et<=ilcaHhQnC7OI}!Ik7Tbje&J_ zYpdtBAgL$6mjfNtAf{P@o5LEbgmlxe${}n8Fn`{?v*^li1Hz6q3M+tue4nM~M_yMg zR85QOYUj%u-WRO7$NV0yMJBg01T~0hmXa%gQjIOmEe#`ouN=hudJkauNW3T*Y(U6Z zqqGMg#*WYmzzYFi9j)=L`BhDMo>`y<(T!5h9zaktvG_P|$`!(6S$hCMSmWz}-~jk| zJb%AQ$>WR4w*YMFDC_|+az3m=Acxj2Gz72$*nCLS1bqu2%_LV{5kD5=XL{puuWA2u z)zIdT!v6utysX!^0D{y}&Qb3Huu2BO2=)K~`Bw-M0JHsD-A3l0f3DQg*PdN#Zu~IB zZ0r4>fY$t?wM5R-d<#%A2=sHyy`2L9L?#epeqRV^NsxH2f8_;4_^-00000NkvXXu0mjf8f4B- delta 939 zcmV;c162I>2f+uBB!7rWL_t(|UhSI8YZFlv#?M*=i(sp<+B7v)yRcBY2r3$1NTE=y zq6?93>_(^yaUp_R7cK;M;!4o}Ko^3$qAM2`iVup9wluarif9+5wsp*zs z5=s1i@X=-_lRNk3eBa!;6CwZr000000000000000{AWB)c7M_Ho)o=2)<&O3T4~{K znpQ`9==ZyWIez+R zl4efsh!naOzps+^T$tlvsv4O6nar+@XHk(cuhkfUIzx(7rtIUH6pdxt{cUXI*Qa;V zhmkbBz0ysSH+yOJP7i&(wU6en?V=y&H&Z?!Xl}SUFn>IYLqmeYCMilY2&Vc9K~N56 z5zqA}=*y{=U>m>wcBa{70+090vE{Z`3LXR2%-Jmw^G^&U{C#mCeY7dp*Xr!ERyD=| z)G21M%9Nc8gw4HlV-w9?UAvmI%t8{Q+stmv6s$6XV0Nfbh^}IkeNwdMRbI|@VW=rE zfO%e8ynjC~1F*4|Z@2i85HQ1#Z=d|_*PAL10dowLtlx(znZ4P@ppyQB2EYh_kin%` z0z=C!K=?Mmtx^<2zz70oG3uh%SNTw}>+?{G(*VA?^ICb7_NB*+|6jh>8oaL()EGE} zIP^U^zAG7nO8XBQ0M`s)s()+f%C7@L7ha8K0DsoD{ld4j;Zj@%5M!^`e)0U`41h@g zBc^v4Kkoj620+Yjt^tf6NQQzz2Lu<5Ouf0VtGxzb6o1gRYX-2M8O`Fa=NW*$-w*Y@ zL=eQ;f6$<4a)3Y^Lf#2bD9fPaiO zc_{$^000000000000000000000000003-ka0000000000004k`;1Bp-;HOYoN}>P& N002ovPDHLkV1l*AvReQE diff --git a/source/core/assets/images/projectiles/mobKing_projectile.atlas b/source/core/assets/images/projectiles/mobKing_projectile.atlas new file mode 100644 index 000000000..ae151e03b --- /dev/null +++ b/source/core/assets/images/projectiles/mobKing_projectile.atlas @@ -0,0 +1,62 @@ + +mobKing_projectile.png +size: 256, 32 +format: RGBA8888 +filter: Nearest, Nearest +repeat: none +mob_boss + rotate: false + xy: 142, 2 + size: 26, 19 + orig: 26, 19 + offset: 0, 0 + index: -1 +mob_boss + rotate: false + xy: 198, 3 + size: 24, 18 + orig: 24, 18 + offset: 0, 0 + index: -1 +mob_boss + rotate: false + xy: 170, 2 + size: 26, 19 + orig: 26, 19 + offset: 0, 0 + index: -1 +mob_boss + rotate: false + xy: 37, 2 + size: 33, 19 + orig: 33, 19 + offset: 0, 0 + index: -1 +mob_boss + rotate: false + xy: 107, 2 + size: 33, 19 + orig: 33, 19 + offset: 0, 0 + index: -1 +mob_boss + rotate: false + xy: 72, 2 + size: 33, 19 + orig: 33, 19 + offset: 0, 0 + index: -1 +mob_bossFinal + rotate: false + xy: 2, 2 + size: 33, 19 + orig: 33, 19 + offset: 0, 0 + index: -1 +default + rotate: false + xy: 142, 2 + size: 26, 19 + orig: 26, 19 + offset: 0, 0 + index: -1 diff --git a/source/core/assets/images/projectiles/mobKing_projectile.png b/source/core/assets/images/projectiles/mobKing_projectile.png new file mode 100644 index 0000000000000000000000000000000000000000..167bf6e4e8fb78807c60e62387ac9928964ff3eb GIT binary patch literal 1351 zcmV-N1-SZ&P)b7X zqYd6xK~O;px2rtSb8`&*{NPZ1e`;SJ^n9uFq++Rt`e3wy8?>E}9-|?G{~0Y_E#!@p zziZne-sgdyU%lYxr>EwR+pJ>STn~@*^Q1BP98>yWw0RTKhR-R6Kv0K@JzL1nuKld- zFSfZAx{>fQyMJ_d$Z*4HftS9{Z1(mf@%edW*p2Ojp{C^~uEjNNef*IW9kY}JK(9qg zK~FT1R=id$_Q=wr!gjAhPxvUs1E{Ic*;7|P8do)y4+ahU>fGkc&S#JBF8(cjXzZMp zbqHAQP+K_-tv%-Q+NZ2m0#2G0)N?9QQ&F_FR~FmG@-B4tKx!O7x*wkG_wna?TN3g2 z#0P`sXCHiR_!?2nQrL(6c0OpoV{1lpqb^lmFF=Mh`OWpOwZ=DHUHh`UL!c<0d6J%- z*ez^Jp1qoUxU&0-f}-KV(WFCt3LUXk_YT&Z&7?)%zvtF;emZxk$YzLAKrF1&Evf>*=xDDMp@NKz=02dbi zjO5}_p{PNNnkNpkXR63Ry<_p+8&Ic@^}(foXF9s*u#n-oqYk_mA_Il+qxos!GpF+I{05^%syGb}Nx?WmKs~k50-)Mf0 zW9Ki{=(e^=p+wgH0?>ldMC);?E`@I37*g$=rj#GVp?Hbp5>yYxp z#}pbo0QmfncavbvsdecZacp0BZ5(f`we2aC*tzT`)7uL>cT)8&0v9uji_YUNtf9UN zRW9}-KkUhVx642RP4U&sSUwo_^!WI$FT|6WrTCb_ckhn@wMkUoO@j4K^(nxVYui(h ztYUHH^Nqv#qR$nY=jX6M{FPE4DvyGY*yYD>>P2IF`(VV=whaKiK5cSSXz&0iZuX%m zN{w+7aH{VC=-N2O7;D>8sGuqZil1}IW_f?-onc>)e(zq3AKQl(gvKpiZB<=`hIc z8-rAwl*aObbJ}2eeZKs15KEiB*7!r3|9?Omf6T!a^gx?UH3b?orLobbZmS>&Rt# Date: Sat, 9 Sep 2023 16:38:18 +1000 Subject: [PATCH 45/72] changed the projectile called in the range boss task --- .../csse3200/game/components/tasks/RangeBossMovementTask.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java index 6518d00e7..b2906848e 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java @@ -57,7 +57,7 @@ public void start() { public void update() { if (currentTask.getStatus() != Status.ACTIVE) { if (currentTask == movementTask) { - Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), new Vector2(0, (currentPos.y + 0.75f)), new Vector2(2f,2f)); + Entity newProjectile = ProjectileFactory.createFireball(owner.getEntity(), new Vector2(0, (currentPos.y + 0.75f)), new Vector2(2f,2f)); newProjectile.scaleHeight(-0.4f); newProjectile.setPosition((float) (currentPos.x), (float) (currentPos.y+0.75f)); ServiceLocator.getEntityService().register(newProjectile); @@ -88,4 +88,4 @@ private void swapTask(Task newTask) { currentTask.start(); } -} \ No newline at end of file +} From 4a6823b06917fe9a396c9592aa0393029438207d Mon Sep 17 00:00:00 2001 From: ThivanW <140519988+ThivanW@users.noreply.github.com> Date: Sat, 9 Sep 2023 16:41:32 +1000 Subject: [PATCH 46/72] Fixed capitalization on createFireBall call --- .../csse3200/game/components/tasks/RangeBossMovementTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java index b2906848e..1c6897069 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java @@ -57,7 +57,7 @@ public void start() { public void update() { if (currentTask.getStatus() != Status.ACTIVE) { if (currentTask == movementTask) { - Entity newProjectile = ProjectileFactory.createFireball(owner.getEntity(), new Vector2(0, (currentPos.y + 0.75f)), new Vector2(2f,2f)); + Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), new Vector2(0, (currentPos.y + 0.75f)), new Vector2(2f,2f)); newProjectile.scaleHeight(-0.4f); newProjectile.setPosition((float) (currentPos.x), (float) (currentPos.y+0.75f)); ServiceLocator.getEntityService().register(newProjectile); From cba4021ef637ba61d09fb03d662ba345aa3d0135 Mon Sep 17 00:00:00 2001 From: ThivanW <140519988+ThivanW@users.noreply.github.com> Date: Sat, 9 Sep 2023 16:53:04 +1000 Subject: [PATCH 47/72] Changed the createFireBall code to match what is called in the main branch --- .../csse3200/game/components/tasks/RangeBossMovementTask.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java index 1c6897069..87f51b1a3 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java @@ -7,6 +7,7 @@ import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.ProjectileFactory; import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.physics.PhysicsLayer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +58,8 @@ public void start() { public void update() { if (currentTask.getStatus() != Status.ACTIVE) { if (currentTask == movementTask) { - Entity newProjectile = ProjectileFactory.createFireBall(owner.getEntity(), new Vector2(0, (currentPos.y + 0.75f)), new Vector2(2f,2f)); + Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.OBSTACLE, new Vector2(0, currentPos.y + 0.75f), new Vector2(2f,2f)); + newProjectile.scaleHeight(-0.4f); newProjectile.setPosition((float) (currentPos.x), (float) (currentPos.y+0.75f)); ServiceLocator.getEntityService().register(newProjectile); From 7ae87490f5ffff1efeccff43876583144cbb9f16 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Sat, 9 Sep 2023 17:00:42 +1000 Subject: [PATCH 48/72] Commenting out the test projectile spawninging in ForestGameArea --- .../core/src/main/com/csse3200/game/areas/ForestGameArea.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 a16d7d231..b926b8715 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -162,7 +162,7 @@ public void create() { // Types of projectile spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.HUMANS, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); - spawnMobBall(new Vector2(0, 8), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); + // spawnProjectileTest(new Vector2(0, 8), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); spawnXenoGrunts(); @@ -300,7 +300,7 @@ private void spawnProjectile(Vector2 position, short targetLayer, int direction, * @param speed The speed of the projectiles. * */ - private void spawnMobBall(Vector2 position, short targetLayer, int direction, Vector2 speed) { + 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); From 551e09ff80b4bf3482e611847cb2e3647058de26 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Sat, 9 Sep 2023 17:35:20 +1000 Subject: [PATCH 49/72] Spawn 2 income turrets instead of 50 --- .../com/csse3200/game/areas/ForestGameArea.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 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 b318c879b..525f56db0 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -179,20 +179,13 @@ public void create() { spawnGhosts(); spawnWeaponTower(); - - - - spawnIncome(); - + spawnTNTTower(); spawnDroidTower(); - - - spawnEngineer(); bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); - spawnTNTTower(); + } @@ -533,7 +526,7 @@ private void spawnIncome() { GridPoint2 minPos = new GridPoint2(0, 0); GridPoint2 maxPos = terrain.getMapBounds(0).sub(2, 2); - for (int i = 0; i < 50; i++) { + for (int i = 0; i < 2; i++) { GridPoint2 randomPos = RandomUtils.random(minPos, maxPos); Entity towerfactory = TowerFactory.createIncomeTower(); spawnEntityAt(towerfactory, randomPos, true, true); From d58efa3d42ff6a6003b7dd80733fa7bee99eb499 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Sat, 9 Sep 2023 17:35:55 +1000 Subject: [PATCH 50/72] Spawn 2 income turrets instead of 50 --- source/core/src/main/com/csse3200/game/areas/ForestGameArea.java | 1 + 1 file changed, 1 insertion(+) 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 525f56db0..6cbc1aa1f 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -182,6 +182,7 @@ public void create() { spawnTNTTower(); spawnDroidTower(); spawnEngineer(); + spawnIncome(); bossKing1 = spawnBossKing1(); bossKing2 = spawnBossKing2(); From c2fe451fa9e30178baaa314c82df436b55ba4556 Mon Sep 17 00:00:00 2001 From: freshc0w <121275444+freshc0w@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:18:57 +1000 Subject: [PATCH 51/72] Fix worldIsLocked == false collision and finalise ricochet component for projectile --- .../game/components/RicochetComponent.java | 59 ++++++++++++------- .../components/tasks/TowerCombatTask.java | 2 +- .../com/csse3200/game/entities/Entity.java | 8 +-- .../entities/factories/ProjectileFactory.java | 3 +- .../csse3200/game/physics/PhysicsEngine.java | 36 +++++++---- 5 files changed, 68 insertions(+), 40 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/RicochetComponent.java b/source/core/src/main/com/csse3200/game/components/RicochetComponent.java index f0445abdc..33a8f3bb6 100644 --- a/source/core/src/main/com/csse3200/game/components/RicochetComponent.java +++ b/source/core/src/main/com/csse3200/game/components/RicochetComponent.java @@ -1,28 +1,32 @@ package com.csse3200.game.components; - import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.physics.box2d.Body; import com.badlogic.gdx.physics.box2d.Fixture; -import com.csse3200.game.ai.tasks.AITaskComponent; -import com.csse3200.game.components.tasks.TrajectTask; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.ProjectileFactory; 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.PhysicsComponent; import com.csse3200.game.services.ServiceLocator; /** * Ricochet based on target layers. - * Basically a bouncing effect that continues to bounce off entities - * Possible extensions: Have a limitied amount of bounce until self-destruct. + * A bouncing effect that continues to bounce off desired entities. */ public class RicochetComponent extends Component { private short targetLayer; private HitboxComponent hitBoxComponent; private int bounceCount; + private static int MAX_BOUNCE_Y_DIRECTION = 250; + private static int MIN_BOUNCE_Y_DIRECTION = -250; + /** + * Initialise a RicochetComponent that spawns another projectile upon collision. + * Projectile has a chance to head upwards or downwards and upon spawning, + * it will be slighlty up or down in respect to original disappearance. + * @param targetLayer Target layer upon collision + * @param bounceCount Keeps track of the bounce count upon initial collision + * Stops self-spawning when bounce count is greater or equal than two. + */ public RicochetComponent(short targetLayer, int bounceCount) { this.targetLayer = targetLayer; this.bounceCount = bounceCount; @@ -33,28 +37,43 @@ public void create() { entity.getEvents().addListener("collisionEnd", this::onCollisionEnd); hitBoxComponent = entity.getComponent(HitboxComponent.class); } - + /** - * After collision ends, make another fireball that spawns just before the original one. This assumes - * that the original fireball is already deleted. Set TouchAttackComponent disposeOnHit to true. + * After collision ends, make another fireball that spawns just before the + * original one. This assumes + * that the original fireball is already deleted. Set TouchAttackComponent + * disposeOnHit to true. + * * @param me * @param other */ private void onCollisionEnd(Fixture me, Fixture other) { - if(hitBoxComponent.getFixture() != me) return; - - if(!PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits)) return; + if (hitBoxComponent.getFixture() != me + || !PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits) + || bounceCount >= 2) // BounceCount base case + return; - if(bounceCount >= 2) return; - Entity projectile = ((BodyUserData) me.getBody().getUserData()).entity; - Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, projectile.getPosition().y + getRandomNumFrom(-250, 250)), new Vector2(2f, 2f), ++bounceCount); - newProjectile.setPosition((float) (projectile.getPosition().x -1.5), (float) (projectile.getPosition().y)); + // Projectile heads upwards or downwards. + int randomDirection = getRandomNumFrom(MIN_BOUNCE_Y_DIRECTION, MAX_BOUNCE_Y_DIRECTION); + + // Spawning of the projectile to be above (+ve) or below (-ve) upon + // collision + int up_or_down = randomDirection <= 0 ? -1 : 1; + + float newXPosition = (float) (projectile.getPosition().x - 0.75); + float newYPosition = (float) (projectile.getPosition().y + (0.65 * up_or_down)); + + // Prevent spawn of new projectile if it goes out of boundaries. + if (newYPosition >= 8 || newYPosition <= 1 || newXPosition >= 17 || newXPosition <= 1) + return; + + Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, + new Vector2(100, projectile.getPosition().y + randomDirection), new Vector2(2f, 2f), ++bounceCount); // Increment bounceCount + + newProjectile.setPosition(newXPosition, newYPosition); - // projectile.getComponent(AITaskComponent.class).addTask(new TrajectTask(new Vector2(100, projectile.getPosition().y + 50))); - - ServiceLocator.getEntityService().register(newProjectile); } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index ff8c81aeb..3841e5c46 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -113,7 +113,7 @@ public void updateTowerState() { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state // Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - // Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + // Entity newProjectile = ProjectileFactory.createPierceFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); // * TEMPORARYYYYYYY Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f), 0); diff --git a/source/core/src/main/com/csse3200/game/entities/Entity.java b/source/core/src/main/com/csse3200/game/entities/Entity.java index 0cb9842f6..138c8ed61 100644 --- a/source/core/src/main/com/csse3200/game/entities/Entity.java +++ b/source/core/src/main/com/csse3200/game/entities/Entity.java @@ -261,10 +261,10 @@ public void update() { createdComponents.get(i).triggerUpdate(); } - if (isFlaggedForDelete) { - dispose(); - return; - } + // if (isFlaggedForDelete) { + // dispose(); + // return; + // } } /** diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index 7bdacfee5..ce3c0e1e7 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -10,7 +10,6 @@ import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.DeleteOnMapEdgeComponent; -import com.csse3200.game.components.MobProjectileAnimationController; import com.csse3200.game.entities.configs.BaseEntityConfig; import com.csse3200.game.entities.configs.NPCConfigs; import com.csse3200.game.files.FileLoader; @@ -91,7 +90,7 @@ public static Entity createRicochetFireball(short targetLayer, Vector2 destinati fireBall .addComponent(new RicochetComponent(targetLayer, bounceCount)); - setColliderSize(fireBall, (float) 0.4, (float) 0.15); + setColliderSize(fireBall, (float) 0.1, (float) 0.1); return fireBall; } diff --git a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java index 882ad4935..5f6168a1f 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java @@ -45,7 +45,7 @@ public PhysicsEngine(World world, GameTime timeSource) { public void update() { // Check for deleted bodies and joints - // checkAndDeleteBodies(); + checkAndDeleteBodies(); // Updating physics isn't as easy as triggering an update every frame. Each frame could take a // different amount of time to run, but physics simulations are only stable if computed at a @@ -90,21 +90,31 @@ public void checkAndDeleteBodies() { world.getBodies(bodies); - // Check for bodies to be deleted - for (Body body : bodies) { - // check for null values - if (body.getUserData() != null) { - Entity entity = ((BodyUserData) body.getUserData()).entity; - // If the entity is flagged for deletion, destroy the body before world.step() is called - if (entity.getFlagForDelete()) { - logger.debug("Destroying physics body {}", body); - ProjectileDestructors.destroyProjectile(entity); - - // Make sure not to delete the body twice - entity.setFlagForDelete(false); + // ! CANNOT USE ITERATOR HERE + // ! If you do: "ERROR: #ITERATOR CAN'T BE NESTED" + for(int i = 0; i < bodies.size; i++) { + if(bodies.get(i) != null || bodies.get(i).getUserData() != null) { + Entity entity = ((BodyUserData) bodies.get(i).getUserData()).entity; + if(entity.getFlagForDelete()) { + entity.dispose(); } } } + // Check for bodies to be deleted + // for (Body body : bodies) { + // // check for null values + // if (body.getUserData() != null) { + // Entity entity = ((BodyUserData) body.getUserData()).entity; + // // If the entity is flagged for deletion, destroy the body before world.step() is called + // if (entity.getFlagForDelete()) { + // logger.debug("Destroying physics body {}", body); + // ProjectileDestructors.destroyProjectile(entity); + + // // Make sure not to delete the body twice + // entity.setFlagForDelete(false); + // } + // } + // } } public World getWorld() { From 1b630202eee776b8738195c298d3978653cf91ef Mon Sep 17 00:00:00 2001 From: freshc0w <121275444+freshc0w@users.noreply.github.com> Date: Sat, 9 Sep 2023 19:39:59 +1000 Subject: [PATCH 52/72] Finalise split firework projectile and insert dummy projectile in towercombattask.java --- .../game/components/RicochetComponent.java | 2 + .../components/SplitFireworksComponent.java | 71 +++++++++++++++++++ .../game/components/TouchAttackComponent.java | 1 + .../components/tasks/TowerCombatTask.java | 27 +++++-- .../entities/factories/ProjectileFactory.java | 9 +++ 5 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 source/core/src/main/com/csse3200/game/components/SplitFireworksComponent.java diff --git a/source/core/src/main/com/csse3200/game/components/RicochetComponent.java b/source/core/src/main/com/csse3200/game/components/RicochetComponent.java index 33a8f3bb6..533e1ae48 100644 --- a/source/core/src/main/com/csse3200/game/components/RicochetComponent.java +++ b/source/core/src/main/com/csse3200/game/components/RicochetComponent.java @@ -69,10 +69,12 @@ private void onCollisionEnd(Fixture me, Fixture other) { if (newYPosition >= 8 || newYPosition <= 1 || newXPosition >= 17 || newXPosition <= 1) return; + // * RIGHT NOW TARGET IS NPC, SUBJECT TO CHANGE Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, projectile.getPosition().y + randomDirection), new Vector2(2f, 2f), ++bounceCount); // Increment bounceCount newProjectile.setPosition(newXPosition, newYPosition); + newProjectile.setScale(0.75f, 0.75f); ServiceLocator.getEntityService().register(newProjectile); } diff --git a/source/core/src/main/com/csse3200/game/components/SplitFireworksComponent.java b/source/core/src/main/com/csse3200/game/components/SplitFireworksComponent.java new file mode 100644 index 000000000..a48f6595a --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/SplitFireworksComponent.java @@ -0,0 +1,71 @@ +package com.csse3200.game.components; + +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.physics.box2d.Fixture; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.ProjectileFactory; +import com.csse3200.game.physics.BodyUserData; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.components.HitboxComponent; +import com.csse3200.game.services.ServiceLocator; + +/** + * A component that splits the projectile into multiple mini projectiles. + * Assumes projectile has a disposesOnHit functionality. + */ +public class SplitFireworksComponent extends Component { + private short targetLayer; + private HitboxComponent hitboxComponent; + private int amount; + private static int TOTAL_RANGE = 450; + + /** + * Initialises a component that splits the projectile into multiple fireballs + * upon collision on a specified target layer. + * The spawned projectiles will be spawned just before original projectile + * and spread out in multiple direction set by a certain range. + * Assumes amount of split projectiles is greater or equal than 2. + * + * @param targetLayer Target layer upon collision. + * @param amount Amount of projectiles that is split after collision event. + */ + public SplitFireworksComponent(short targetLayer, int amount) { + this.targetLayer = targetLayer; + this.amount = amount; + } + + @Override + public void create() { + entity.getEvents().addListener("collisionEnd", this::onCollisionEnd); + hitboxComponent = entity.getComponent(HitboxComponent.class); + } + + private void onCollisionEnd(Fixture me, Fixture other) { + if (hitboxComponent.getFixture() != me + || !PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits) + || amount < 2) // Amount of split projectiles must be >= 2 + return; + + Entity projectile = ((BodyUserData) me.getBody().getUserData()).entity; + + for (int i = 0; i < amount; i++) { + int newDirection = (i * TOTAL_RANGE) / (amount - 1); + + // Boundaries + float newXPosition = (float) (projectile.getPosition().x + 1.75); + if (newXPosition >= 18 || newXPosition <= 1) + return; + + // * RIGHT NOW TARGET IS NPC, SUBJECT TO CHANGE + // Speed is a bit faster than normal but can change. + Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, + new Vector2(100, projectile.getPosition().y + (newDirection - (TOTAL_RANGE/2))), new Vector2(3f, 3f)); + + newProjectile.setPosition(newXPosition, (float) projectile.getPosition().y); + + newProjectile.setScale(0.5f, 0.5f); + + ServiceLocator.getEntityService().register(newProjectile); + } + } +} diff --git a/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java b/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java index c42945db3..b84e55b7d 100644 --- a/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java +++ b/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java @@ -11,6 +11,7 @@ /** * When this entity touches a valid enemy's hitbox, deal damage to them and apply a knockback. + * Has an optional disposeOnHit property that disposes projectile upon collision. * *

Requires CombatStatsComponent, HitboxComponent on this entity. * diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 3841e5c46..8248cdd67 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -113,13 +113,26 @@ public void updateTowerState() { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state // Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - // Entity newProjectile = ProjectileFactory.createPierceFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - - // * TEMPORARYYYYYYY - Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f), 0); - - newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); - ServiceLocator.getEntityService().register(newProjectile); + // newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); + // ServiceLocator.getEntityService().register(newProjectile); + + // * TEMPRORARYYYYYYYY + // PIERCE FIREBALL + // Entity pierceFireball = ProjectileFactory.createPierceFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + // pierceFireball.setPosition((float) (owner.getEntity().getPosition().x + 0), (float) (owner.getEntity().getPosition().y + 0.4)); + // ServiceLocator.getEntityService().register(pierceFireball); + + // RICOCHET FIREBALL + // Entity ricochetProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f), 0); + + // ricochetProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0), (float) (owner.getEntity().getPosition().y + 0.4)); + // ServiceLocator.getEntityService().register(ricochetProjectile); + + // SPLIT FIREWORKS FIREBALLL + Entity splitFireWorksProjectile = ProjectileFactory.createSplitFireWorksFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f), 16); + + splitFireWorksProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); + ServiceLocator.getEntityService().register(splitFireWorksProjectile); } } case STOW -> { diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index ce3c0e1e7..fe291d314 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -6,6 +6,7 @@ import com.csse3200.game.components.ProjectileEffects; import com.csse3200.game.components.TouchAttackComponent; import com.csse3200.game.components.RicochetComponent; +import com.csse3200.game.components.SplitFireworksComponent; import com.csse3200.game.components.tasks.TrajectTask; import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.components.CombatStatsComponent; @@ -95,6 +96,14 @@ public static Entity createRicochetFireball(short targetLayer, Vector2 destinati return fireBall; } + public static Entity createSplitFireWorksFireball(short targetLayer, Vector2 destination, Vector2 speed, int amount) { + Entity fireBall = createFireBall(targetLayer, destination, speed); + fireBall + .addComponent(new SplitFireworksComponent(targetLayer, amount)); + + return fireBall; + } + /** * Creates a fireball Entity. * From 7587864c877cd871492be6211cbb39afd3bd5549 Mon Sep 17 00:00:00 2001 From: freshc0w <121275444+freshc0w@users.noreply.github.com> Date: Sat, 9 Sep 2023 19:51:39 +1000 Subject: [PATCH 53/72] delete unecessary commented code from PhysicsEngine --- .../csse3200/game/physics/PhysicsEngine.java | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java index 5f6168a1f..bb7258f4c 100644 --- a/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java +++ b/source/core/src/main/com/csse3200/game/physics/PhysicsEngine.java @@ -93,28 +93,16 @@ public void checkAndDeleteBodies() { // ! CANNOT USE ITERATOR HERE // ! If you do: "ERROR: #ITERATOR CAN'T BE NESTED" for(int i = 0; i < bodies.size; i++) { - if(bodies.get(i) != null || bodies.get(i).getUserData() != null) { - Entity entity = ((BodyUserData) bodies.get(i).getUserData()).entity; - if(entity.getFlagForDelete()) { - entity.dispose(); - } + if(bodies.get(i) != null + && bodies.get(i).getUserData() != null + && (BodyUserData) bodies.get(i).getUserData() != null) { + Entity entity = ((BodyUserData) bodies.get(i).getUserData()).entity; + + if(entity.getFlagForDelete()) { + entity.dispose(); + } } } - // Check for bodies to be deleted - // for (Body body : bodies) { - // // check for null values - // if (body.getUserData() != null) { - // Entity entity = ((BodyUserData) body.getUserData()).entity; - // // If the entity is flagged for deletion, destroy the body before world.step() is called - // if (entity.getFlagForDelete()) { - // logger.debug("Destroying physics body {}", body); - // ProjectileDestructors.destroyProjectile(entity); - - // // Make sure not to delete the body twice - // entity.setFlagForDelete(false); - // } - // } - // } } public World getWorld() { From c862e83c9e3efb73f8c61df101831e7f6a8078e3 Mon Sep 17 00:00:00 2001 From: cindyle1 Date: Sat, 9 Sep 2023 20:06:09 +1000 Subject: [PATCH 54/72] Added animation for an explosion --- .../projectiles/projectile_explosion.atlas | 97 ++++++++++++++++++ .../projectiles/projectile_explosion.png | Bin 0 -> 27228 bytes .../assets/sounds/projectiles/explosion.mp3 | Bin 0 -> 152785 bytes .../sounds/projectiles/on_collision.mp3 | Bin 0 -> 38143 bytes .../csse3200/game/areas/ForestGameArea.java | 15 ++- .../ExplosionAnimationController.java | 44 ++++++++ .../OnCollisionAnimationController.java | 23 +++++ .../ProjectileAnimationController.java | 2 - .../entities/factories/ProjectileFactory.java | 16 ++- 9 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 source/core/assets/images/projectiles/projectile_explosion.atlas create mode 100644 source/core/assets/images/projectiles/projectile_explosion.png create mode 100644 source/core/assets/sounds/projectiles/explosion.mp3 create mode 100644 source/core/assets/sounds/projectiles/on_collision.mp3 create mode 100644 source/core/src/main/com/csse3200/game/components/projectile/ExplosionAnimationController.java create mode 100644 source/core/src/main/com/csse3200/game/components/projectile/OnCollisionAnimationController.java diff --git a/source/core/assets/images/projectiles/projectile_explosion.atlas b/source/core/assets/images/projectiles/projectile_explosion.atlas new file mode 100644 index 000000000..d285fa835 --- /dev/null +++ b/source/core/assets/images/projectiles/projectile_explosion.atlas @@ -0,0 +1,97 @@ + +projectile_explosion.png +size: 2048, 256 +format: RGBA8888 +filter: Nearest, Nearest +repeat: none +explosion + rotate: false + xy: 1042, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 782, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 262, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosionFinal + rotate: false + xy: 1302, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 392, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 1432, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 652, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 132, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 1172, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 522, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 2, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +explosion + rotate: false + xy: 912, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +default + rotate: false + xy: 1042, 2 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 diff --git a/source/core/assets/images/projectiles/projectile_explosion.png b/source/core/assets/images/projectiles/projectile_explosion.png new file mode 100644 index 0000000000000000000000000000000000000000..cc46711cfd6e4bec4409cf34d815e1a8f268d268 GIT binary patch literal 27228 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 z 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 fUk&_!YJj=sWV2kBBQ2_^L2tMuNRRHgS`Md>14 z1Qi=9e}DImJI43$t%tMsK6{Ka*2A7_uDSL)+GuGo;6LNV85?W-8|eT560A*tqpYHY zl)Qu_68S&7|NFSUGWh?j{{N?DE}l;RcK&?^Fa!YMN&zHf6jZeIOss62HxPV+B4RhC zWEGTetD?0qx_X85!;`b~FPA@l{ki_nlYc(j{qtE`{=dcl86Szo|4je+ z;STuS{C}_iZ@j+Rwg&)!l_dHfApnS2AKR@$Jdl?sTl?pF3h-N2#cQQ=0D#Kf8DNOe zUnCG>Z(HqzZvWP&`v4@T=qHK2ZFwCvsx8V>ah;G>L?p?@}j@y?y4t@B@=>BW+%^QCkQ9^0UR#AE=JOF|sg_i^v z?j&_NVP!PMN{H-$xIMT4vWJ9(6anoJqs&z3B2of~u!tS-)mt?P(hG$iYIXWRg3ber zDF>$TzhTqHzDfBKx%D;hCLX^Fuq)=h&&3OR^x2)th^&X1nVnEre(cceu~CrOnfDqt zYesm_@Rd(x`eU0gaW}ChVOu@%DuiUgMjHkMp<_y_q^`S~qf!sQYPk&;op5jPFt3$1 z2v}*8E(ch#izHY2|4g><5xk?srn0~DQ+L+rV@LUW@lC+9(iT%^gd~R-`NG5AiTf?z zK58CFPWQ}|z4|TD;O4nLBdHMZ&@t9<^w=jW8y>3+a)!Q-0ZcY(Bld5Z<+Uf8})W$r!1K&sC@6=V)f^Me^^c00AhWH-Gcy4;!#3)w!i1 z$GAHLh4HAWxDvn=CydBX*tZGhATkgtZE2~==xt@J0aWGOJSjFp)ceqZKpPyJ08M`> zJ$gY1^4F<2=cQwh4MPb_;8+&QE6J-SyRl)_EP9jgDt%pNx0Hj;l5D1KtR>m?b zDgr9BlWyib8IhE;fLFK0sDfj^$wVu}^9U2H6RnkDi*N0q&acQgZG6Ubc_hRl`Cp+y ze*G+z1VDtG`VdeflyyypSv3@bmw*qJ^V#x}5R?wfZHK$=4!EjAvexE5IQ&N558S;1|VRrzGj3Lxy-4Ra?u@>Stceo1M zggYk_b`0$MhE(I63D8m^CBYm894dR89V${X0!{YI_F#{023gbe1sa=h%XCviig@B_ z%($|O+?P?*K<8rBpz(-|B)DxkIh{|@-o}GZgZMI$%dLlQB0`<&K)I+H1s3+8*l_{J zRi0Z-)h*wT+9Ds#nlwbztV)(o#QeE=+oj=7a)+ZVyoO`KP^^JJ6C+(Q(H0ZZ`BdH?N7dZ2YMN^1n7*pAM--ibXN$i4|n_86a z>AM%%4Wb2pmH|%WYOaHytp*Ju+aip5eXe9oidhk!8l>8mt~`2nX$u9*6N6EcJC#%S znvOY|j-7i9FPo@Qi_7+sl|x^B?nzT*ZY5q{D_?&iOR7xzNS#n#I~L}O#+<&mTXYE> zQ3HWah)+a0s#t8B%oEg!Bx1SRGE!=?N#IvxvLvu`oWo#II_Wk-k}yv6f`+e^E;uv5 zF^fbg8!B=&idbFwdHrdFqL$D~j5(20><6c}$@HXA%)Ke3(Qz>*j^qJ3lQ?7>+`$b% z!n(-%ni|EZMbKR3S%voaSo^NY{iPR7HI8He?-8YyLz_yyGXb}wYLt|3_byQ(x0T0HKYcKt?05W_tl;? zsw9Ke)w~VPcOsgoN5oyf*!qt{{@1)VasxhYzV5lxyV-R1^7k1>-OTfaWDwrAfB9i0 zcq2xYi6R~kCW3%qT#a|1dUnME%6NvV6Ris2ItRY{KP$?{!E~5CrY9xAVN1sTsnl>u;oyugbN&yK zp~4ir1E_n(?!Pt0)aFB{bh7fZhY|R82C3i(hVWlW*HUk&`nK5JBbC?J9#{m2st}DvunE6uJx-%_H?k7G{@bf4->fU8a#QmH#BkD%(1-RjFf?UA&*|piSiX=doWJ?=Wh+wIBl6ltb?`SsFc`kl5yhT4(A9ZH#a@EcJ12@f->J#hhlGz;0{Y?9=FPEM`BIK

T7FH5Y{aip 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

QX?a&a7WhYcuD3! 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 Gy8i)Sb~3mC literal 0 HcmV?d00001 diff --git a/source/core/assets/sounds/projectiles/on_collision.mp3 b/source/core/assets/sounds/projectiles/on_collision.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..af209d670df110e9e44d0ab4a67edb88dd35f253 GIT binary patch literal 38143 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�mxJM^j!6$JbD%H`SBp^lKIu5^OY+F8^jFw5 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 xgSt98jQ`)k|K^MNzGo$if`JPB)_cJT> Date: Sat, 9 Sep 2023 20:09:49 +1000 Subject: [PATCH 55/72] add temporary spawn fireball functions in forest game area and jsdocs --- .../csse3200/game/areas/ForestGameArea.java | 52 +++++++++++++++++++ .../game/components/RicochetComponent.java | 2 +- .../components/tasks/TowerCombatTask.java | 14 ++--- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java index 7e4fd626b..7c4a39650 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -162,6 +162,9 @@ public void create() { // Types of projectile spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.HUMANS, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); + spawnPierceFireBall(new Vector2(2, 3), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); + spawnRicochetFireball(new Vector2(2, 4), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); + spawnSplitFireWorksFireBall(new Vector2(2, 5), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f), 12); // spawnProjectileTest(new Vector2(0, 8), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); spawnXenoGrunts(); @@ -421,6 +424,55 @@ private void spawnEffectProjectile(Vector2 position, short targetLayer, int dire 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 head 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(2, 2); diff --git a/source/core/src/main/com/csse3200/game/components/RicochetComponent.java b/source/core/src/main/com/csse3200/game/components/RicochetComponent.java index 533e1ae48..4203dfa6a 100644 --- a/source/core/src/main/com/csse3200/game/components/RicochetComponent.java +++ b/source/core/src/main/com/csse3200/game/components/RicochetComponent.java @@ -50,7 +50,7 @@ public void create() { private void onCollisionEnd(Fixture me, Fixture other) { if (hitBoxComponent.getFixture() != me || !PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits) - || bounceCount >= 2) // BounceCount base case + || bounceCount >= 3) // BounceCount base case of 3 return; Entity projectile = ((BodyUserData) me.getBody().getUserData()).entity; diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 8248cdd67..566e04568 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -112,11 +112,11 @@ public void updateTowerState() { } else { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state - // Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - // newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); - // ServiceLocator.getEntityService().register(newProjectile); + Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); + ServiceLocator.getEntityService().register(newProjectile); - // * TEMPRORARYYYYYYYY + // * TEMPRORARYYYYYYYY PLS DON'T DELETE THIS // PIERCE FIREBALL // Entity pierceFireball = ProjectileFactory.createPierceFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); // pierceFireball.setPosition((float) (owner.getEntity().getPosition().x + 0), (float) (owner.getEntity().getPosition().y + 0.4)); @@ -129,10 +129,10 @@ public void updateTowerState() { // ServiceLocator.getEntityService().register(ricochetProjectile); // SPLIT FIREWORKS FIREBALLL - Entity splitFireWorksProjectile = ProjectileFactory.createSplitFireWorksFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f), 16); + // Entity splitFireWorksProjectile = ProjectileFactory.createSplitFireWorksFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f), 16); - splitFireWorksProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); - ServiceLocator.getEntityService().register(splitFireWorksProjectile); + // splitFireWorksProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); + // ServiceLocator.getEntityService().register(splitFireWorksProjectile); } } case STOW -> { From b35878704e5450970f8a4f17af8de0763db5c3df Mon Sep 17 00:00:00 2001 From: cindyle1 Date: Sat, 9 Sep 2023 21:10:00 +1000 Subject: [PATCH 56/72] Tested size and speed of MobKingBall. Added implementation to RangeBossMovementTask --- .../tasks/RangeBossMovementTask.java | 23 ++++++++++++++++--- .../game/components/tasks/TrajectTask.java | 1 + .../entities/factories/ProjectileFactory.java | 11 +++++---- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java index 87f51b1a3..f18d89c94 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java @@ -23,6 +23,13 @@ public class RangeBossMovementTask extends DefaultTask implements PriorityTask { private MovementTask movementTask; private WaitTask waitTask; private Task currentTask; + /** Animation event names */ + private static final String START = "startMobKing"; + private static final String FINAL = "startMobKingFinal"; + private enum STATE { + START, FINAL + } + private STATE bossBallState = STATE.START; /** * @param waitTime How long in seconds to wait between wandering. @@ -54,13 +61,23 @@ public void start() { this.owner.getEntity().getEvents().trigger("rangeBossMovementStart"); } + public void switchMobKingBallState() { + switch (bossBallState) { + case START: + owner.getEntity().getEvents().trigger(FINAL); + bossBallState = STATE.FINAL; + } + } + @Override public void update() { if (currentTask.getStatus() != Status.ACTIVE) { if (currentTask == movementTask) { - Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.OBSTACLE, new Vector2(0, currentPos.y + 0.75f), new Vector2(2f,2f)); - - newProjectile.scaleHeight(-0.4f); + Entity newProjectile = ProjectileFactory.createMobKingBall( + PhysicsLayer.HUMANS, new Vector2(0, currentPos.y + 0.75f), new Vector2(2f,2f)); + owner.getEntity().getEvents().trigger(START); + switchMobKingBallState(); + newProjectile.scaleHeight(-0.6f); newProjectile.setPosition((float) (currentPos.x), (float) (currentPos.y+0.75f)); ServiceLocator.getEntityService().register(newProjectile); startWaiting(); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TrajectTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TrajectTask.java index 7b25061f5..04967ba8e 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TrajectTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TrajectTask.java @@ -40,6 +40,7 @@ public void start() { this.owner.getEntity().getEvents().trigger(START); this.owner.getEntity().getEvents().trigger("rotate"); this.owner.getEntity().getEvents().trigger("start"); + this.owner.getEntity().getEvents().trigger("startMobKing"); } public void switchProjectileState() { diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index 0ac4646e2..bd6cf53e6 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -7,6 +7,7 @@ import com.csse3200.game.components.TouchAttackComponent; import com.csse3200.game.components.RicochetComponent; import com.csse3200.game.components.SplitFireworksComponent; +import com.csse3200.game.components.projectile.MobKingProjectAnimController; import com.csse3200.game.components.tasks.TrajectTask; import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.components.CombatStatsComponent; @@ -202,16 +203,16 @@ public static Entity createMobKingBall(short targetLayer, Vector2 destination, V new AnimationRenderComponent( ServiceLocator.getResourceService() .getAsset("images/projectiles/mobKing_projectile.atlas", TextureAtlas.class)); - animator.addAnimation("mob_boss", 0.1f, Animation.PlayMode.NORMAL); - animator.addAnimation("mob_bossFinal", 0.1f, Animation.PlayMode.NORMAL); + animator.addAnimation("mob_boss", 0.17f, Animation.PlayMode.NORMAL); + animator.addAnimation("mob_bossFinal", 0.17f, Animation.PlayMode.NORMAL); projectile .addComponent(animator) - .addComponent(new MobProjectileAnimationController()); + .addComponent(new MobKingProjectAnimController()); -// projectile -// .getComponent(AnimationRenderComponent.class).scaleEntity(); + projectile + .getComponent(AnimationRenderComponent.class).scaleEntity(); return projectile; } From 52bd5880c11db39e5f651dcffda5410943b58296 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Sat, 9 Sep 2023 21:51:02 +1000 Subject: [PATCH 57/72] Adjusted size of Mob King Balls and of Base Projectiles --- .../csse3200/game/components/tasks/RangeBossMovementTask.java | 3 ++- .../com/csse3200/game/components/tasks/TowerCombatTask.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java index f18d89c94..be0e655c6 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/RangeBossMovementTask.java @@ -77,7 +77,8 @@ public void update() { PhysicsLayer.HUMANS, new Vector2(0, currentPos.y + 0.75f), new Vector2(2f,2f)); owner.getEntity().getEvents().trigger(START); switchMobKingBallState(); - newProjectile.scaleHeight(-0.6f); + // newProjectile.scaleHeight(-1f); + newProjectile.setScale(-1.3f, 0.82f); newProjectile.setPosition((float) (currentPos.x), (float) (currentPos.y+0.75f)); ServiceLocator.getEntityService().register(newProjectile); startWaiting(); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 566e04568..913752be1 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -113,7 +113,8 @@ public void updateTowerState() { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.4)); + newProjectile.setScale(1.1f, 0.8f); + newProjectile.setPosition((float) (owner.getEntity().getPosition().x + 0.75), (float) (owner.getEntity().getPosition().y + 0.5)); ServiceLocator.getEntityService().register(newProjectile); // * TEMPRORARYYYYYYYY PLS DON'T DELETE THIS From 0a11fabefc9a08c4b0ca840abba574317e83a246 Mon Sep 17 00:00:00 2001 From: gregchan550 Date: Sat, 9 Sep 2023 22:36:11 +1000 Subject: [PATCH 58/72] Implemented slow effects but game crashes occasionally upon clicking the planet --- .../csse3200/game/areas/ForestGameArea.java | 2 +- .../game/components/EffectsComponent.java | 97 ++++++++++++------- .../components/tasks/TowerCombatTask.java | 6 +- 3 files changed, 66 insertions(+), 39 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 bd215d6b5..40b428bea 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -160,7 +160,7 @@ public void create() { playMusic(); // Types of projectile - spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.HUMANS, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); + spawnEffectProjectile(new Vector2(0, 3), PhysicsLayer.ALL, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, false); spawnMobBall(new Vector2(15,10), PhysicsLayer.NPC, towardsTowers, new Vector2(2f, 2f)); spawnXenoGrunts(); 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 a68bfff67..fab70250d 100644 --- a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java @@ -1,5 +1,6 @@ package com.csse3200.game.components; +import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.Fixture; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.ProjectileFactory; @@ -7,14 +8,15 @@ import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.components.ColliderComponent; import com.csse3200.game.physics.components.HitboxComponent; +import com.csse3200.game.physics.components.PhysicsComponent; +import com.csse3200.game.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; -import java.util.ArrayList; - /** * This component applies an effect from the ProjectileEffects enum. This consists of fireball, burn, * slow, and stun. Component also handles the targeting of specific layers and an area of effect @@ -26,7 +28,7 @@ public class EffectsComponent extends Component { private final boolean aoe; private HitboxComponent hitboxComponent; private final short targetLayer; - private ArrayList burnEntities = new ArrayList<>(); + private Array burnEntities = new Array<>(); /** * Constructor for the AoEComponent. @@ -141,13 +143,9 @@ public void applyAoeEffect(ProjectileEffects effect) { CombatStatsComponent targetCombatStats = targetEntity.getComponent(CombatStatsComponent.class); if (targetCombatStats != null) { switch (effect) { - case FIREBALL -> { - fireballEffect(targetCombatStats, hostCombatStats); - } - case BURN -> { - burnEffect(targetCombatStats, hostCombatStats); - } - case SLOW -> {} + case FIREBALL -> {fireballEffect(targetCombatStats, hostCombatStats);} + case BURN -> {burnEffect(targetCombatStats, hostCombatStats);} + case SLOW -> {slowEffect(targetEntity);} case STUN -> {} } } else { @@ -172,7 +170,7 @@ private void fireballEffect(CombatStatsComponent target, CombatStatsComponent ho */ private void burnEffect(CombatStatsComponent target, CombatStatsComponent host) { // Ensure burn effects aren't applied multiple times by same projectile - if (burnEntities.contains(target)) { + if (burnEntities.contains(target, false)) { return; } burnEntities.add(target); @@ -196,29 +194,56 @@ public void run() { }, delay, delay); } -// private void slowEffect(Entity targetEntity) { -// // 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) { -// // Check if projectile is meant for towers or mobs -// if (!PhysicsLayer.contains(PhysicsLayer.HUMANS, targetEntity.getComponent(HitboxComponent.class).getLayer())) { -// // towers -// targetEntity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.FIRERATE, -30); -// } else if (!PhysicsLayer.contains(PhysicsLayer.NPC, targetEntity.getComponent(HitboxComponent.class).getLayer())) { -// // mobs -// } -// 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.ALL, 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 + } } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java index 752a67859..04c85a8f0 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/TowerCombatTask.java @@ -3,6 +3,7 @@ import com.badlogic.gdx.math.Vector2; import com.csse3200.game.ai.tasks.DefaultTask; import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.components.ProjectileEffects; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.ProjectileFactory; import com.csse3200.game.physics.PhysicsEngine; @@ -112,8 +113,9 @@ public void updateTowerState() { } else { owner.getEntity().getEvents().trigger(FIRING); // this might be changed to an event which gets triggered everytime the tower enters the firing state - Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); - + //Entity newProjectile = ProjectileFactory.createFireBall(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); + // * Greg testing + Entity newProjectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f), ProjectileEffects.BURN, false); // * TEMPORARYYYYYYY // Entity newProjectile = ProjectileFactory.createRicochetFireball(PhysicsLayer.NPC, new Vector2(100, owner.getEntity().getPosition().y), new Vector2(2f,2f)); From 28bd40f4067b83f0a125b70e9de0868eb89bb2f5 Mon Sep 17 00:00:00 2001 From: gregchan550 Date: Sat, 9 Sep 2023 22:52:54 +1000 Subject: [PATCH 59/72] added slow case to switch statements in effects component and added in target parameter toa pply single effect --- .../game/components/EffectsComponent.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 fab70250d..c7cc10b12 100644 --- a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java @@ -83,10 +83,16 @@ private void onCollisionEnd(Fixture me, Fixture other) { if (aoe) { applyAoeEffect(ProjectileEffects.BURN); } else { - applySingleEffect(ProjectileEffects.BURN, otherCombatStats); + applySingleEffect(ProjectileEffects.BURN, otherCombatStats, otherEntity); + } + } + case SLOW -> { + if (aoe) { + applyAoeEffect(ProjectileEffects.SLOW); + } else { + applySingleEffect(ProjectileEffects.SLOW, otherCombatStats, otherEntity); } } - case SLOW -> {} case STUN -> {} } } @@ -95,7 +101,7 @@ private void onCollisionEnd(Fixture me, Fixture other) { * Used for singe targeting projectiles to apply effects entity it collides with. * @param effect effect to be applied to entity */ - public void applySingleEffect(ProjectileEffects effect, CombatStatsComponent targetCombatStats) { + public void applySingleEffect(ProjectileEffects effect, CombatStatsComponent targetCombatStats, Entity targetEntity) { Entity hostEntity = getEntity(); CombatStatsComponent hostCombatStats = hostEntity.getComponent(CombatStatsComponent.class); @@ -110,7 +116,7 @@ public void applySingleEffect(ProjectileEffects effect, CombatStatsComponent tar case BURN -> { burnEffect(targetCombatStats, hostCombatStats); } - case SLOW -> {} + case SLOW -> {slowEffect(targetEntity);} case STUN -> {} } } @@ -213,7 +219,7 @@ private void slowEffect(Entity targetEntity) { // towers towerFlag = true; //targetEntity.getEvents().trigger("upgradeTower", TowerUpgraderComponent.UPGRADE.FIRERATE, -30); - } else if (!PhysicsLayer.contains(PhysicsLayer.ALL, targetEntity.getComponent(HitboxComponent.class).getLayer())) { + } else if (!PhysicsLayer.contains(PhysicsLayer.NPC, targetEntity.getComponent(HitboxComponent.class).getLayer())) { // mobs mobFlag = true; targetPhysics = targetEntity.getComponent(PhysicsMovementComponent.class); From 6b0f06efc89ebc527ee1ebfb06d1c2234ca008a7 Mon Sep 17 00:00:00 2001 From: gregchan550 Date: Sat, 9 Sep 2023 23:21:00 +1000 Subject: [PATCH 60/72] Fixed if statement for detecting layers in slowEffect --- .../main/com/csse3200/game/components/EffectsComponent.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c7cc10b12..843d65288 100644 --- a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java @@ -215,11 +215,11 @@ private void slowEffect(Entity targetEntity) { float ySpeed = 0; // Create a timer task to apply the effect repeatedly - if (!PhysicsLayer.contains(PhysicsLayer.HUMANS, targetEntity.getComponent(HitboxComponent.class).getLayer())) { + 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())) { + } else if (PhysicsLayer.contains(PhysicsLayer.NPC, targetEntity.getComponent(HitboxComponent.class).getLayer())) { // mobs mobFlag = true; targetPhysics = targetEntity.getComponent(PhysicsMovementComponent.class); From c23cab88a84f335ca6ba8a27491a215ba49ea617 Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Sat, 9 Sep 2023 23:58:40 +1000 Subject: [PATCH 61/72] Added snow ball design and animation to slow effect projectile --- .../assets/images/projectiles/snow_ball.atlas | 48 ++++++++++++++++++ .../assets/images/projectiles/snow_ball.png | Bin 0 -> 882 bytes .../csse3200/game/areas/ForestGameArea.java | 9 ++-- ...SnowBallProjectileAnimationController.java | 31 +++++++++++ .../entities/factories/ProjectileFactory.java | 18 ++++++- 5 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 source/core/assets/images/projectiles/snow_ball.atlas create mode 100644 source/core/assets/images/projectiles/snow_ball.png create mode 100644 source/core/src/main/com/csse3200/game/components/projectile/SnowBallProjectileAnimationController.java diff --git a/source/core/assets/images/projectiles/snow_ball.atlas b/source/core/assets/images/projectiles/snow_ball.atlas new file mode 100644 index 000000000..20e4eb554 --- /dev/null +++ b/source/core/assets/images/projectiles/snow_ball.atlas @@ -0,0 +1,48 @@ + +snow_ball.png +size: 128, 32 +format: RGBA8888 +filter: Nearest, Nearest +repeat: none +projectile + rotate: false + xy: 20, 2 + size: 16, 19 + orig: 16, 19 + offset: 0, 0 + index: -1 +projectile + rotate: false + xy: 56, 2 + size: 16, 19 + orig: 16, 19 + offset: 0, 0 + index: -1 +projectile + rotate: false + xy: 38, 2 + size: 16, 19 + orig: 16, 19 + offset: 0, 0 + index: -1 +projectileFinal + rotate: false + xy: 2, 2 + size: 16, 19 + orig: 16, 19 + offset: 0, 0 + index: -1 +collision + rotate: false + xy: 92, 3 + size: 16, 18 + orig: 16, 18 + offset: 0, 0 + index: -1 +collision + rotate: false + xy: 74, 3 + size: 16, 18 + orig: 16, 18 + offset: 0, 0 + index: -1 diff --git a/source/core/assets/images/projectiles/snow_ball.png b/source/core/assets/images/projectiles/snow_ball.png new file mode 100644 index 0000000000000000000000000000000000000000..f110a1e025596e004db09983af6c9bbd3600e3b8 GIT binary patch literal 882 zcmV-&1C9KNP)Otxu)AP7cWd9kUo%S=jXr(H-W?A4c3vE9^X4SasLHE@w@?LvSgUVAkp z^!nTOZ^~JBZ8T;nIM1%4T&oye*v>$%T~z??>$VCiyT0B>Ef??SpWc6Ber++BK#HFe-eDoI2+E&mHUmeSJ;EsRrX%GwIi~r;l zx&LMupX@HgT%70e$)rVoXnH`<1lw3zPH%dGVxVW9Q zzGlS&fO%1BI|r_7F~V+r81DEd-};14_R4*9cVoYoB*NkQUGCW4M`1tjV0k7Ona3%a zoCM1kz#*tV+KdBA!Yf_`r8RL}&H5;!ClPlxuaVAAMvwaYy`(|3(e=Bp2OmYRS$r>o z$yzmr*l7IhWG1RY1=WoJ7hlI@KJP35QoBA~i^i#q<$FmY8o&GXdJbOavw+5+`7?y0 zF;+hTRHPa_X;J{^J^_ff9ru&*t>sRD626yo84I8$QHm$c3Sd$QF8;0pfFI{5!D|#* z#?Sc-(8Bmf`4yl^vBekhq!Sgl#uUc#hd>w2`{3)pNDzCjUjaw`5SUB0el7tYPrBkl z=gdKz($ByWB53xV1lM=ydw?_*ub<~=eg^JJcCL@?OZs_W66SglL?He--UJ@siy&}F zvdJMyKMRPr#H--5kTQ2M=0A#8!JsDDWUZtFjSY)`1}#WUve!&H;XLv0;1$Rvv}9`~ zo%sB2YvAJ4+J(cDTe8cHi|iHzK@bE%5ClOG1VIo4LGb_Z7wW|Wyj!nr82|tP07*qo IM6N<$g8pouBme*a literal 0 HcmV?d00001 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 3a7147167..2286528d9 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -101,7 +101,9 @@ public class ForestGameArea extends GameArea { "images/projectiles/basic_projectile.png", "images/projectiles/mobProjectile.png", "images/projectiles/engineer_projectile.png", - "images/projectiles/mobKing_projectile.png" + "images/projectiles/mobKing_projectile.png", + "images/projectiles/snow_ball.png" + }; private static final String[] forestTextureAtlases = { "images/economy/econ-tower.atlas", @@ -117,7 +119,8 @@ public class ForestGameArea extends GameArea { "images/projectiles/basic_projectile.atlas", "images/projectiles/mobProjectile.atlas", "images/projectiles/engineer_projectile.atlas", - "images/projectiles/mobKing_projectile.atlas" + "images/projectiles/mobKing_projectile.atlas", + "images/projectiles/snow_ball.atlas" }; private static final String[] forestSounds = { "sounds/Impact4.ogg", @@ -168,10 +171,10 @@ public void create() { playMusic(); // Types of projectile - spawnEffectProjectile(new Vector2(0, 10), PhysicsLayer.HUMANS, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.BURN, true); spawnPierceFireBall(new Vector2(2, 3), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); spawnRicochetFireball(new Vector2(2, 4), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); spawnSplitFireWorksFireBall(new Vector2(2, 5), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f), 12); + spawnEffectProjectile(new Vector2(2, 6), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f), ProjectileEffects.SLOW, false); // spawnProjectileTest(new Vector2(0, 8), PhysicsLayer.NPC, towardsMobs, new Vector2(2f, 2f)); spawnXenoGrunts(); diff --git a/source/core/src/main/com/csse3200/game/components/projectile/SnowBallProjectileAnimationController.java b/source/core/src/main/com/csse3200/game/components/projectile/SnowBallProjectileAnimationController.java new file mode 100644 index 000000000..adb1f2869 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/projectile/SnowBallProjectileAnimationController.java @@ -0,0 +1,31 @@ +package com.csse3200.game.components.projectile; + +import com.csse3200.game.components.Component; +import com.csse3200.game.rendering.AnimationRenderComponent; + +public class SnowBallProjectileAnimationController extends Component{ + private static final String START = "startProjectile"; + private static final String FINAL = "startProjectileFinal"; + + /** Animation name constants */ + private static final String START_ANIM = "projectile"; + private static final String FINAL_ANIM = "projectileFinal"; + AnimationRenderComponent animator; + + @Override + public void create() { + super.create(); + animator = this.entity.getComponent(AnimationRenderComponent.class); + entity.getEvents().addListener(START, this::animateStart); + entity.getEvents().addListener(FINAL, this::animateFinal); + + } + + void animateStart() { + animator.startAnimation(START_ANIM); + } + + void animateFinal() { + animator.startAnimation(FINAL_ANIM); + } +} diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index bd6cf53e6..0a606a6a7 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -28,6 +28,7 @@ import com.csse3200.game.components.projectile.EngineerBulletsAnimationControlller; import com.csse3200.game.components.projectile.MobProjectileAnimationController; import com.csse3200.game.components.projectile.ProjectileAnimationController; +import com.csse3200.game.components.projectile.SnowBallProjectileAnimationController; /** * Responsible for creating projectiles within the game. @@ -53,7 +54,7 @@ public class ProjectileFactory { * @return Returns a new single-target projectile entity */ public static Entity createEffectProjectile(short targetLayer, Vector2 destination, Vector2 speed, ProjectileEffects effect, boolean aoe) { - Entity projectile = createFireBall(targetLayer, destination, speed); + Entity projectile = createBaseProjectile(targetLayer, destination, speed); switch(effect) { case FIREBALL -> { @@ -64,6 +65,21 @@ public static Entity createEffectProjectile(short targetLayer, Vector2 destinati } case SLOW -> { projectile.addComponent(new EffectsComponent(targetLayer, 3, ProjectileEffects.SLOW, aoe)); + AnimationRenderComponent animator = + new AnimationRenderComponent( + ServiceLocator.getResourceService() + .getAsset("images/projectiles/snow_ball.atlas", TextureAtlas.class)); + animator.addAnimation(START_ANIM, START_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(FINAL_ANIM, FINAL_SPEED, Animation.PlayMode.NORMAL); + + projectile + .addComponent(animator) + .addComponent(new SnowBallProjectileAnimationController()); + // * TEMPORARY + // .addComponent(new DeleteOnMapEdgeComponent()); + // .addComponent(new SelfDestructOnHitComponent(PhysicsLayer.OBSTACLE)); + + return projectile; } case STUN -> { projectile.addComponent(new EffectsComponent(targetLayer, 3, ProjectileEffects.STUN, aoe)); From f2cef9123b61c66931d4fe63afdb8229b816676b Mon Sep 17 00:00:00 2001 From: MiniSoda17 Date: Sun, 10 Sep 2023 01:00:13 +1000 Subject: [PATCH 62/72] Fixed a typo for EngineerBulletAnimationController and refined piercing projectile --- .../game/components/TouchAttackComponent.java | 4 +++ ...> EngineerBulletsAnimationController.java} | 2 +- .../entities/factories/ProjectileFactory.java | 25 +++++++++++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) rename source/core/src/main/com/csse3200/game/components/projectile/{EngineerBulletsAnimationControlller.java => EngineerBulletsAnimationController.java} (91%) diff --git a/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java b/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java index b84e55b7d..9570f7d25 100644 --- a/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java +++ b/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java @@ -104,6 +104,10 @@ private void onCollisionStart(Fixture me, Fixture other) { public void setDisposeOnHit(boolean disposeOnHit) { this.disposeOnHit = disposeOnHit; } + + public void setKnockBack(float knockback) { + this.knockbackForce = knockback; + } // private void onCollisionEnd(Fixture me, Fixture other) { // // Nothing to do on collision end // } diff --git a/source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationControlller.java b/source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationController.java similarity index 91% rename from source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationControlller.java rename to source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationController.java index ad8d0fd57..a9dc1a63f 100644 --- a/source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationControlller.java +++ b/source/core/src/main/com/csse3200/game/components/projectile/EngineerBulletsAnimationController.java @@ -4,7 +4,7 @@ import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; //used for sound -public class EngineerBulletsAnimationControlller extends Component{ +public class EngineerBulletsAnimationController extends Component{ /** Event name constants */ AnimationRenderComponent animator; diff --git a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java index 0a606a6a7..152685fdb 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/ProjectileFactory.java @@ -25,7 +25,7 @@ import com.csse3200.game.physics.components.PhysicsComponent; import com.csse3200.game.physics.components.PhysicsMovementComponent; import com.badlogic.gdx.math.Vector2; -import com.csse3200.game.components.projectile.EngineerBulletsAnimationControlller; +import com.csse3200.game.components.projectile.EngineerBulletsAnimationController; import com.csse3200.game.components.projectile.MobProjectileAnimationController; import com.csse3200.game.components.projectile.ProjectileAnimationController; import com.csse3200.game.components.projectile.SnowBallProjectileAnimationController; @@ -59,9 +59,29 @@ public static Entity createEffectProjectile(short targetLayer, Vector2 destinati switch(effect) { case FIREBALL -> { projectile.addComponent(new EffectsComponent(targetLayer, 3, ProjectileEffects.FIREBALL, aoe)); + AnimationRenderComponent animator = + new AnimationRenderComponent( + ServiceLocator.getResourceService() + .getAsset(BASE_PROJECTILE_ATLAS, TextureAtlas.class)); + animator.addAnimation(START_ANIM, START_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(FINAL_ANIM, FINAL_SPEED, Animation.PlayMode.NORMAL); + + projectile + .addComponent(animator) + .addComponent(new ProjectileAnimationController()); } case BURN -> { projectile.addComponent(new EffectsComponent(targetLayer, 3, ProjectileEffects.BURN, aoe)); + AnimationRenderComponent animator = + new AnimationRenderComponent( + ServiceLocator.getResourceService() + .getAsset(BASE_PROJECTILE_ATLAS, TextureAtlas.class)); + animator.addAnimation(START_ANIM, START_SPEED, Animation.PlayMode.NORMAL); + animator.addAnimation(FINAL_ANIM, FINAL_SPEED, Animation.PlayMode.NORMAL); + + projectile + .addComponent(animator) + .addComponent(new ProjectileAnimationController()); } case SLOW -> { projectile.addComponent(new EffectsComponent(targetLayer, 3, ProjectileEffects.SLOW, aoe)); @@ -95,6 +115,7 @@ public static Entity createEffectProjectile(short targetLayer, Vector2 destinati public static Entity createPierceFireBall(short targetLayer, Vector2 destination, Vector2 speed) { Entity fireBall = createFireBall(targetLayer, destination, speed); fireBall.getComponent(TouchAttackComponent.class).setDisposeOnHit(false); + fireBall.getComponent(TouchAttackComponent.class).setKnockBack(0f); return fireBall; } @@ -168,7 +189,7 @@ public static Entity createEngineerBullet(short targetLayer, Vector2 destination projectile .addComponent(animator) - .addComponent(new EngineerBulletsAnimationControlller()); + .addComponent(new EngineerBulletsAnimationController()); // .addComponent(new SelfDestructOnHitComponent(PhysicsLayer.OBSTACLE)); return projectile; From fb1466a8cd4ac56d7c5a51d3dd0ac442dd778377 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Sun, 10 Sep 2023 01:12:48 +1000 Subject: [PATCH 63/72] Added JUnit tests for DroidAnimationController and TNTDamageComponent --- .../tower/DroidAnimationController.java | 2 +- .../tower/DroidAnimationControllerTest.java | 84 +++++++++++++ .../tower/TNTDamageComponentTest.java | 119 ++++++++++++++++++ 3 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 source/core/src/test/com/csse3200/game/components/tower/DroidAnimationControllerTest.java create mode 100644 source/core/src/test/com/csse3200/game/components/tower/TNTDamageComponentTest.java diff --git a/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java index e1fb8f098..be123d483 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/tower/DroidAnimationController.java @@ -81,6 +81,6 @@ void animateDeath() { * Triggers the "default" animation for the entity. * This method should be invoked when the entity returns to its default state. */ - void animateDefault() { animator.startAnimation("default");} + void animateDefault() { animator.startAnimation("idle");} } diff --git a/source/core/src/test/com/csse3200/game/components/tower/DroidAnimationControllerTest.java b/source/core/src/test/com/csse3200/game/components/tower/DroidAnimationControllerTest.java new file mode 100644 index 000000000..666c9767a --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tower/DroidAnimationControllerTest.java @@ -0,0 +1,84 @@ +package com.csse3200.game.components.tower; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.TowerFactory; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.rendering.AnimationRenderComponent; +import com.csse3200.game.rendering.DebugRenderer; +import com.csse3200.game.rendering.RenderService; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(GameExtension.class) +public class DroidAnimationControllerTest { + + private Entity mockEntity; + private final String[] texture = {"images/towers/DroidTower.png"}; + private final String[] atlas = {"images/towers/DroidTower.atlas"}; + + @BeforeEach + public void setUp() { + ServiceLocator.registerPhysicsService(new PhysicsService()); + RenderService render = new RenderService(); + render.setDebug(mock(DebugRenderer.class)); + ServiceLocator.registerRenderService(render); + ResourceService resourceService = new ResourceService(); + ServiceLocator.registerResourceService(resourceService); + resourceService.loadTextures(texture); + resourceService.loadTextureAtlases(atlas); + resourceService.loadAll(); + + mockEntity = TowerFactory.createDroidTower(); // Replace with actual Droid Tower creation logic + mockEntity.create(); + } + + @Test + public void testAnimateWalk() { + mockEntity.getEvents().trigger("walkStart"); + assertEquals("walk", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateDefault() { + mockEntity.getEvents().trigger("idleStart"); + assertEquals("idle", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateGoUp() { + mockEntity.getEvents().trigger("goUpStart"); + assertEquals("goUp", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateGoDown() { + mockEntity.getEvents().trigger("goDownStart"); + assertEquals("goDown", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateAttackUp() { + mockEntity.getEvents().trigger("attackUpStart"); + assertEquals("attackUp", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateAttackDown() { + mockEntity.getEvents().trigger("attackDownStart"); + assertEquals("attackDown", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } + + @Test + public void testAnimateDeath() { + mockEntity.getEvents().trigger("deathStart"); + assertEquals("death", mockEntity.getComponent(AnimationRenderComponent.class).getCurrentAnimation()); + } +} diff --git a/source/core/src/test/com/csse3200/game/components/tower/TNTDamageComponentTest.java b/source/core/src/test/com/csse3200/game/components/tower/TNTDamageComponentTest.java new file mode 100644 index 000000000..1b8f988aa --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tower/TNTDamageComponentTest.java @@ -0,0 +1,119 @@ +package com.csse3200.game.components.tower; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.EntityService; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.physics.components.HitboxComponent; +import com.csse3200.game.physics.components.PhysicsComponent; +import com.csse3200.game.rendering.DebugRenderer; +import com.csse3200.game.rendering.RenderService; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(GameExtension.class) +public class TNTDamageComponentTest { + + private GameTime gameTime; + private Entity Attacker; + private Entity Target_1; + private Entity Target_2; + private Entity Entity_3; + private final String[] texture = {"images/towers/TNTTower.png"}; + private final String[] atlas = {"images/towers/TNTTower.atlas"}; + + @BeforeEach + public void setUp() { + gameTime = mock(GameTime.class); + when(gameTime.getDeltaTime()).thenReturn(1f); + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + ServiceLocator.registerEntityService(new EntityService()); + RenderService render = new RenderService(); + render.setDebug(mock(DebugRenderer.class)); + ServiceLocator.registerRenderService(render); + ResourceService resourceService = new ResourceService(); + ServiceLocator.registerResourceService(resourceService); + resourceService.loadTextures(texture); + resourceService.loadTextureAtlases(atlas); + resourceService.loadAll(); + Attacker = createAttacker((short) (1 << 3)); + Target_1 = createTarget((short) (1 << 3)); + Target_2 = createTarget((short) (1 << 3)); + Entity_3 = createTarget((short) (1 << 4)); + ServiceLocator.getEntityService().register(Attacker); + ServiceLocator.getEntityService().register(Target_1); + ServiceLocator.getEntityService().register(Target_2); + ServiceLocator.getEntityService().register(Entity_3); + + + } + + @Test + public void TestTNTDamageOnTargetsThatAreInRange() { + Attacker.setPosition(10,10); + Target_1.setPosition(12,10);// Same lane and inside radius + + Attacker.getEvents().trigger("TNTDamageStart"); + + assertEquals(80, Target_1.getComponent(CombatStatsComponent.class).getHealth()); + + + } + + @Test + public void TestTNTDamageOnTargetsThatAreNoTInRange() { + Attacker.setPosition(10,10); + Target_1.setPosition(15,10); // on the same lane but outside the radius + Target_2.setPosition(11,12); // inside the radius but outside the lane + + + Attacker.getEvents().trigger("TNTDamageStart"); + //Nothing should happen + assertEquals(100, Target_2.getComponent(CombatStatsComponent.class).getHealth()); + assertEquals(100, Target_1.getComponent(CombatStatsComponent.class).getHealth()); + } + + @Test + public void TestTNTDamageOnEntitiesThatAreNotTargets() { + Attacker.setPosition(10,10); + Entity_3.setPosition(12,10); // on the same lane and inside the radius but different target layer + + + Attacker.getEvents().trigger("TNTDamageStart"); + //Nothing should happen + assertEquals(100, Entity_3.getComponent(CombatStatsComponent.class).getHealth()); + + } + + Entity createAttacker(short targetLayer) { + Entity entity = + new Entity() + .addComponent(new CombatStatsComponent(100, 20)) + .addComponent(new PhysicsComponent()) + .addComponent(new HitboxComponent()) + .addComponent(new TNTDamageComponent(targetLayer,0,4)); + + return entity; + } + + Entity createTarget(short layer) { + Entity target = + new Entity() + .addComponent(new CombatStatsComponent(100, 10)) + .addComponent(new PhysicsComponent()) + .addComponent(new HitboxComponent().setLayer(layer)); + + return target; + } +} + + From cff12b9258a0bc9d677d9d055b2087cbc59c4117 Mon Sep 17 00:00:00 2001 From: Kevin <104761532+Hasakev@users.noreply.github.com> Date: Sun, 10 Sep 2023 01:28:13 +1000 Subject: [PATCH 64/72] Implemented base code of Turret Selection --- .../src/main/com/csse3200/game/GdxGame.java | 4 +- .../components/mainmenu/MainMenuActions.java | 2 +- .../com/csse3200/game/entities/Entity.java | 3 + .../csse3200/game/screens/StoryScreen.java | 10 ++ .../game/screens/TurretSelectionScreen.java | 95 +++++++++++++++++++ 5 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java diff --git a/source/core/src/main/com/csse3200/game/GdxGame.java b/source/core/src/main/com/csse3200/game/GdxGame.java index 146fb71c7..ed04864d0 100644 --- a/source/core/src/main/com/csse3200/game/GdxGame.java +++ b/source/core/src/main/com/csse3200/game/GdxGame.java @@ -74,13 +74,15 @@ private Screen newScreen(ScreenType screenType) { return new StoryScreen(this); case LEVEL_SELECT: return new LevelSelectScreen(this); + case TURRET_SELECTION: + return new TurretSelectionScreen(this); default: return null; } } public enum ScreenType { - MAIN_MENU, MAIN_GAME, SETTINGS, STORY_SCREEN, LEVEL_SELECT + MAIN_MENU, MAIN_GAME, SETTINGS, STORY_SCREEN, LEVEL_SELECT, TURRET_SELECTION } /** diff --git a/source/core/src/main/com/csse3200/game/components/mainmenu/MainMenuActions.java b/source/core/src/main/com/csse3200/game/components/mainmenu/MainMenuActions.java index 6907caf67..188c717b8 100644 --- a/source/core/src/main/com/csse3200/game/components/mainmenu/MainMenuActions.java +++ b/source/core/src/main/com/csse3200/game/components/mainmenu/MainMenuActions.java @@ -30,7 +30,7 @@ public void create() { */ private void onStart() { logger.info("Start game"); - game.setScreen(GdxGame.ScreenType.STORY_SCREEN); + game.setScreen(GdxGame.ScreenType.TURRET_SELECTION); // game.setScreen(GdxGame.ScreenType.LEVEL_SELECT); } diff --git a/source/core/src/main/com/csse3200/game/entities/Entity.java b/source/core/src/main/com/csse3200/game/entities/Entity.java index c26ba6f00..403f34abe 100644 --- a/source/core/src/main/com/csse3200/game/entities/Entity.java +++ b/source/core/src/main/com/csse3200/game/entities/Entity.java @@ -314,4 +314,7 @@ public int setLayer(int layer) { return layer; } + public String getName() { + return this.getClass().getSimpleName(); + } } diff --git a/source/core/src/main/com/csse3200/game/screens/StoryScreen.java b/source/core/src/main/com/csse3200/game/screens/StoryScreen.java index 760e39e19..7f81669f9 100644 --- a/source/core/src/main/com/csse3200/game/screens/StoryScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/StoryScreen.java @@ -89,6 +89,16 @@ public void render(float delta) { stage.draw(); } + /** + * Fixes the + * @param width + * @param height + */ + @Override + public void resize(int width, int height) { + stage.getViewport().update(width, height, true); + } + @Override public void dispose() { batch.dispose(); diff --git a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java new file mode 100644 index 000000000..3fa892a4a --- /dev/null +++ b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java @@ -0,0 +1,95 @@ +package com.csse3200.game.screens; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.ScreenAdapter; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.scenes.scene2d.InputEvent; +import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.Skin; +import com.badlogic.gdx.scenes.scene2d.ui.Table; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; +import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.viewport.ScreenViewport; +import com.csse3200.game.GdxGame; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.factories.TowerFactory; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; + +import java.util.*; + +public class TurretSelectionScreen extends ScreenAdapter { + + private Stage stage; + private List turretList; + private TextButton confirmButton; + + private GdxGame game; + + private Set selectedTurrets = new HashSet<>(); + + public TurretSelectionScreen(GdxGame game) { + this.game = game; + stage = new Stage(new ScreenViewport()); + Table table = new Table(); + + turretList = new ArrayList<>(); + // Add turrets to the list + + String weaponTower = "test"; + String incomeTower = "test2"; + turretList.add(weaponTower); + turretList.add(incomeTower); + + Skin skin = new Skin(Gdx.files.internal("flat-earth/skin/flat-earth-ui.json")); + confirmButton = new TextButton("Continue", skin); + confirmButton.addListener(new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + game.setScreen(GdxGame.ScreenType.MAIN_GAME); + } + }); + + + + for (String turret : turretList) { + TextButton turretButton = new TextButton(turret, skin); + turretButton.addListener(new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + if (selectedTurrets.contains(turret)) { + // Turret is already selected, unselect it + selectedTurrets.remove(turret); + // You can also change the button appearance to indicate unselection + } else { + // Turret is not selected, select it + selectedTurrets.add(turret); + // You can change the button appearance to indicate selection + } + } + }); + table.add(turretButton).row(); + } + table.add(confirmButton).padBottom(-400).row(); + + stage.addActor(table); + table.setFillParent(true); + Gdx.input.setInputProcessor(stage); + + } + @Override + public void render(float delta) { + Gdx.gl.glClearColor(0, 0, 0, 1); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + + stage.draw(); + } + + @Override + public void dispose() { + stage.dispose(); + } + +} From f369560458c3c307931beb18030668fde9c1ecdd Mon Sep 17 00:00:00 2001 From: Mohamad Date: Sun, 10 Sep 2023 02:01:30 +1000 Subject: [PATCH 65/72] Increased the Droid health because it's dying quickly --- source/core/assets/configs/tower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/core/assets/configs/tower.json b/source/core/assets/configs/tower.json index 333eb8d32..c7a95392e 100644 --- a/source/core/assets/configs/tower.json +++ b/source/core/assets/configs/tower.json @@ -20,7 +20,7 @@ "cost": 1 }, "DroidTower": { - "health": 10, + "health": 50, "baseAttack": 5, "cost": 1 }, From 49a23d4af9c42a9ae3568b70892c710ed819a233 Mon Sep 17 00:00:00 2001 From: Mohamad Date: Sun, 10 Sep 2023 02:02:00 +1000 Subject: [PATCH 66/72] Integrated the slow-effect projectile with the Droid --- .../game/components/tasks/DroidCombatTask.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java b/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java index 8512c2221..57b91f06e 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/DroidCombatTask.java @@ -4,6 +4,7 @@ import com.csse3200.game.ai.tasks.DefaultTask; import com.csse3200.game.ai.tasks.PriorityTask; import com.csse3200.game.components.CombatStatsComponent; +import com.csse3200.game.components.ProjectileEffects; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.factories.ProjectileFactory; import com.csse3200.game.physics.PhysicsEngine; @@ -112,6 +113,12 @@ public void updateTowerState() { case SHOOT_DOWN -> { if (isTargetVisible()) { owner.getEntity().getEvents().trigger(ATTACK_DOWN); + Entity Projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100, + owner.getEntity().getPosition().y), new Vector2(2,2), ProjectileEffects.SLOW, false); + Projectile.setScale(new Vector2(0.5f,0.5f)); + Projectile.setPosition((float) (owner.getEntity().getPosition().x + 0.2), + (float) (owner.getEntity().getPosition().y - 0.2)); + ServiceLocator.getEntityService().register(Projectile); towerState = STATE.UP; } else { owner.getEntity().getEvents().trigger(GO_UP); @@ -123,6 +130,12 @@ public void updateTowerState() { owner.getEntity().getEvents().trigger(ATTACK_UP); towerState = STATE.DOWN; + Entity Projectile = ProjectileFactory.createEffectProjectile(PhysicsLayer.NPC, new Vector2(100, + owner.getEntity().getPosition().y), new Vector2(2,2), ProjectileEffects.SLOW, false); + Projectile.setScale(new Vector2(0.5f,0.5f)); + Projectile.setPosition((float) (owner.getEntity().getPosition().x + 0.2), + (float) (owner.getEntity().getPosition().y + 0.5)); + ServiceLocator.getEntityService().register(Projectile); } else { owner.getEntity().getEvents().trigger(IDLE); towerState = STATE.IDLE; From a2af71f57747426ed56a4ee77453c536ca1e976b Mon Sep 17 00:00:00 2001 From: Kevin <104761532+Hasakev@users.noreply.github.com> Date: Sun, 10 Sep 2023 15:56:36 +1000 Subject: [PATCH 67/72] Added enum of turrets for turret selection screen --- .../com/csse3200/game/screens/TowerType.java | 27 +++++++++++++++++++ .../game/screens/TurretSelectionScreen.java | 6 +++++ 2 files changed, 33 insertions(+) create mode 100644 source/core/src/main/com/csse3200/game/screens/TowerType.java diff --git a/source/core/src/main/com/csse3200/game/screens/TowerType.java b/source/core/src/main/com/csse3200/game/screens/TowerType.java new file mode 100644 index 000000000..dfb4fc22f --- /dev/null +++ b/source/core/src/main/com/csse3200/game/screens/TowerType.java @@ -0,0 +1,27 @@ +package com.csse3200.game.screens; + +public enum TowerType { + WEAPON("images/towers/turret01.atlas", "Weapon Tower"), + TNT("images/towers/TNTTower.atlas", "TNT Tower"), + DROID("images/towers/DroidTower.atlas", "Droid Tower"), + WALL("images/towers/wallTower.png", "Wall Tower"), + FIRE("images/towers/fire_tower_atlas.atlas", "Fire Tower"), + STUN("images/towers/stun_tower.atlas", "Stun Tower"), + ECONOMY("images/economy/econ-tower.atlas", "Income Tower"); + + private final String imagePath; + private final String towerName; + + TowerType(String imagePath, String towerName) { + this.imagePath = imagePath; + this.towerName = towerName; + } + + public String getImagePath() { + return imagePath; + } + + public String getTowerName() { + return towerName; + } +} diff --git a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java index 3fa892a4a..da2fe91ad 100644 --- a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java @@ -17,6 +17,8 @@ import com.csse3200.game.physics.PhysicsService; import com.csse3200.game.services.ResourceService; import com.csse3200.game.services.ServiceLocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; @@ -29,6 +31,7 @@ public class TurretSelectionScreen extends ScreenAdapter { private GdxGame game; private Set selectedTurrets = new HashSet<>(); + private static final Logger logger = LoggerFactory.getLogger(MainMenuScreen.class); public TurretSelectionScreen(GdxGame game) { this.game = game; @@ -63,9 +66,12 @@ public void clicked(InputEvent event, float x, float y) { // Turret is already selected, unselect it selectedTurrets.remove(turret); // You can also change the button appearance to indicate unselection + //logger.info(selectedTurrets.toString()); } else { // Turret is not selected, select it selectedTurrets.add(turret); + //logger.info(selectedTurrets.toString()); + // You can change the button appearance to indicate selection } } From 092060c6ea1c3d47f20f1b1c849784876522e40f Mon Sep 17 00:00:00 2001 From: Kevin <104761532+Hasakev@users.noreply.github.com> Date: Sun, 10 Sep 2023 16:09:29 +1000 Subject: [PATCH 68/72] Added towers to turret selectionscreen (text) --- .../com/csse3200/game/screens/TowerType.java | 2 +- .../game/screens/TurretSelectionScreen.java | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/screens/TowerType.java b/source/core/src/main/com/csse3200/game/screens/TowerType.java index dfb4fc22f..c52d6adcb 100644 --- a/source/core/src/main/com/csse3200/game/screens/TowerType.java +++ b/source/core/src/main/com/csse3200/game/screens/TowerType.java @@ -7,7 +7,7 @@ public enum TowerType { WALL("images/towers/wallTower.png", "Wall Tower"), FIRE("images/towers/fire_tower_atlas.atlas", "Fire Tower"), STUN("images/towers/stun_tower.atlas", "Stun Tower"), - ECONOMY("images/economy/econ-tower.atlas", "Income Tower"); + INCOME("images/economy/econ-tower.atlas", "Income Tower"); private final String imagePath; private final String towerName; diff --git a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java index da2fe91ad..aeb51c706 100644 --- a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java @@ -25,12 +25,12 @@ public class TurretSelectionScreen extends ScreenAdapter { private Stage stage; - private List turretList; + private List turretList; private TextButton confirmButton; private GdxGame game; - private Set selectedTurrets = new HashSet<>(); + private Set selectedTurrets = new HashSet<>(); private static final Logger logger = LoggerFactory.getLogger(MainMenuScreen.class); public TurretSelectionScreen(GdxGame game) { @@ -40,11 +40,14 @@ public TurretSelectionScreen(GdxGame game) { turretList = new ArrayList<>(); // Add turrets to the list + turretList.add(TowerType.WEAPON); + turretList.add(TowerType.TNT); + turretList.add(TowerType.DROID); + turretList.add(TowerType.WALL); + turretList.add(TowerType.FIRE); + turretList.add(TowerType.STUN); + turretList.add(TowerType.INCOME); - String weaponTower = "test"; - String incomeTower = "test2"; - turretList.add(weaponTower); - turretList.add(incomeTower); Skin skin = new Skin(Gdx.files.internal("flat-earth/skin/flat-earth-ui.json")); confirmButton = new TextButton("Continue", skin); @@ -57,8 +60,8 @@ public void clicked(InputEvent event, float x, float y) { - for (String turret : turretList) { - TextButton turretButton = new TextButton(turret, skin); + for (TowerType turret : turretList) { + TextButton turretButton = new TextButton(turret.getTowerName(), skin); turretButton.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { @@ -66,11 +69,11 @@ public void clicked(InputEvent event, float x, float y) { // Turret is already selected, unselect it selectedTurrets.remove(turret); // You can also change the button appearance to indicate unselection - //logger.info(selectedTurrets.toString()); + logger.info(selectedTurrets.toString()); } else { // Turret is not selected, select it selectedTurrets.add(turret); - //logger.info(selectedTurrets.toString()); + logger.info(selectedTurrets.toString()); // You can change the button appearance to indicate selection } From eb451384fca9b8be3df5f85010e809abcc32aa30 Mon Sep 17 00:00:00 2001 From: Kevin <104761532+Hasakev@users.noreply.github.com> Date: Mon, 11 Sep 2023 00:36:01 +1000 Subject: [PATCH 69/72] Implemented cap to tower selection (you can only select 5 max) --- .../assets/flat-earth/skin/flat-earth-ui.json | 2 +- .../com/csse3200/game/screens/TowerType.java | 14 +++---- .../game/screens/TurretSelectionScreen.java | 38 +++++++++++++++++-- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/source/core/assets/flat-earth/skin/flat-earth-ui.json b/source/core/assets/flat-earth/skin/flat-earth-ui.json index ba7414e6f..3337baaf1 100644 --- a/source/core/assets/flat-earth/skin/flat-earth-ui.json +++ b/source/core/assets/flat-earth/skin/flat-earth-ui.json @@ -327,7 +327,7 @@ com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle: { default: { font: font - fontColor: black + fontColor: white } button: { font: button diff --git a/source/core/src/main/com/csse3200/game/screens/TowerType.java b/source/core/src/main/com/csse3200/game/screens/TowerType.java index c52d6adcb..deed94733 100644 --- a/source/core/src/main/com/csse3200/game/screens/TowerType.java +++ b/source/core/src/main/com/csse3200/game/screens/TowerType.java @@ -1,13 +1,13 @@ package com.csse3200.game.screens; public enum TowerType { - WEAPON("images/towers/turret01.atlas", "Weapon Tower"), - TNT("images/towers/TNTTower.atlas", "TNT Tower"), - DROID("images/towers/DroidTower.atlas", "Droid Tower"), - WALL("images/towers/wallTower.png", "Wall Tower"), - FIRE("images/towers/fire_tower_atlas.atlas", "Fire Tower"), - STUN("images/towers/stun_tower.atlas", "Stun Tower"), - INCOME("images/economy/econ-tower.atlas", "Income Tower"); + WEAPON("images/towers/turret_deployed.png", "Weapon Tower"), + TNT("images/towers/turret_deployed.png", "TNT Tower"), + DROID("images/towers/turret_deployed.png", "Droid Tower"), + WALL("images/towers/turret_deployed.png", "Wall Tower"), + FIRE("images/towers/turret_deployed.png", "Fire Tower"), + STUN("images/towers/turret_deployed.png", "Stun Tower"), + INCOME("images/towers/turret_deployed.png", "Income Tower"); private final String imagePath; private final String towerName; diff --git a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java index aeb51c706..580a91e2c 100644 --- a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java @@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Stage; +import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; @@ -24,19 +25,25 @@ public class TurretSelectionScreen extends ScreenAdapter { + private static final int MAX_SELECTED_TURRETS = 5; private Stage stage; private List turretList; private TextButton confirmButton; private GdxGame game; + private Label message; + private Label turretsPicked; + private Table table; private Set selectedTurrets = new HashSet<>(); + private static final Logger logger = LoggerFactory.getLogger(MainMenuScreen.class); public TurretSelectionScreen(GdxGame game) { this.game = game; stage = new Stage(new ScreenViewport()); - Table table = new Table(); + table = new Table(); + turretList = new ArrayList<>(); // Add turrets to the list @@ -49,7 +56,11 @@ public TurretSelectionScreen(GdxGame game) { turretList.add(TowerType.INCOME); + Skin skin = new Skin(Gdx.files.internal("flat-earth/skin/flat-earth-ui.json")); + message = new Label("Select your turrets", skin); + turretsPicked = new Label("Turrets picked: ", skin); + confirmButton = new TextButton("Continue", skin); confirmButton.addListener(new ClickListener() { @Override @@ -59,29 +70,48 @@ public void clicked(InputEvent event, float x, float y) { }); - + table.add(message).row(); + table.add(turretsPicked).row(); for (TowerType turret : turretList) { TextButton turretButton = new TextButton(turret.getTowerName(), skin); turretButton.addListener(new ClickListener() { @Override public void clicked(InputEvent event, float x, float y) { + logger.info(String.valueOf(selectedTurrets.size())); + if (selectedTurrets.size() > MAX_SELECTED_TURRETS) { + message.setText("You can only select up to 5 turrets."); + } else { + message.setText("Select your turrets"); + } if (selectedTurrets.contains(turret)) { // Turret is already selected, unselect it selectedTurrets.remove(turret); // You can also change the button appearance to indicate unselection logger.info(selectedTurrets.toString()); - } else { + turretsPicked.setText("Turrets picked: " + selectedTurrets.toString()); + } else if (selectedTurrets.size() == MAX_SELECTED_TURRETS) { + // Turret is not selected, but the max number of turrets has been reached + message.setText("You can only select up to 5 turrets."); + } else if (selectedTurrets.size() < MAX_SELECTED_TURRETS) { + // Turret is not selected, select it + selectedTurrets.add(turret); + turretsPicked.setText("Turrets picked: " + selectedTurrets.toString()); + logger.info(selectedTurrets.toString()); + } + else { // Turret is not selected, select it selectedTurrets.add(turret); + turretsPicked.setText("Turrets picked: " + selectedTurrets.toString()); logger.info(selectedTurrets.toString()); // You can change the button appearance to indicate selection } + } }); table.add(turretButton).row(); } - table.add(confirmButton).padBottom(-400).row(); + table.add(confirmButton).padBottom(20).row(); stage.addActor(table); table.setFillParent(true); From d5ff44112d8a014f69caf4dc3b734af74ada21fb Mon Sep 17 00:00:00 2001 From: Kevin <104761532+Hasakev@users.noreply.github.com> Date: Mon, 11 Sep 2023 01:12:55 +1000 Subject: [PATCH 70/72] Added background to Screen --- .../game/screens/TurretSelectionScreen.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java index 580a91e2c..9effe3933 100644 --- a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java @@ -3,6 +3,9 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.ScreenAdapter; import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.ui.Label; @@ -32,9 +35,14 @@ public class TurretSelectionScreen extends ScreenAdapter { private GdxGame game; + private SpriteBatch batch; + + private Sprite introSprite; + private Label message; private Label turretsPicked; private Table table; + private static final String TEXTURE = "planets/background.png"; private Set selectedTurrets = new HashSet<>(); private static final Logger logger = LoggerFactory.getLogger(MainMenuScreen.class); @@ -44,6 +52,12 @@ public TurretSelectionScreen(GdxGame game) { stage = new Stage(new ScreenViewport()); table = new Table(); + // Set up the background + batch = new SpriteBatch(); + Texture introImage = new Texture(TEXTURE); + introSprite = new Sprite(introImage); + introSprite.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + turretList = new ArrayList<>(); // Add turrets to the list @@ -70,6 +84,7 @@ public void clicked(InputEvent event, float x, float y) { }); + table.add(message).row(); table.add(turretsPicked).row(); for (TowerType turret : turretList) { @@ -122,7 +137,9 @@ public void clicked(InputEvent event, float x, float y) { public void render(float delta) { Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - + batch.begin(); + introSprite.draw(batch); + batch.end(); stage.draw(); } From 7746e5980853d801e2efd205235ef05da93b14f7 Mon Sep 17 00:00:00 2001 From: Kevin <104761532+Hasakev@users.noreply.github.com> Date: Mon, 11 Sep 2023 01:23:21 +1000 Subject: [PATCH 71/72] Integrated Turret selection screen with other starting screens --- .../csse3200/game/components/mainmenu/MainMenuActions.java | 2 +- .../src/main/com/csse3200/game/screens/LevelSelectScreen.java | 2 +- .../main/com/csse3200/game/screens/TurretSelectionScreen.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/mainmenu/MainMenuActions.java b/source/core/src/main/com/csse3200/game/components/mainmenu/MainMenuActions.java index 188c717b8..6907caf67 100644 --- a/source/core/src/main/com/csse3200/game/components/mainmenu/MainMenuActions.java +++ b/source/core/src/main/com/csse3200/game/components/mainmenu/MainMenuActions.java @@ -30,7 +30,7 @@ public void create() { */ private void onStart() { logger.info("Start game"); - game.setScreen(GdxGame.ScreenType.TURRET_SELECTION); + game.setScreen(GdxGame.ScreenType.STORY_SCREEN); // game.setScreen(GdxGame.ScreenType.LEVEL_SELECT); } diff --git a/source/core/src/main/com/csse3200/game/screens/LevelSelectScreen.java b/source/core/src/main/com/csse3200/game/screens/LevelSelectScreen.java index aeddb19b7..69256d192 100644 --- a/source/core/src/main/com/csse3200/game/screens/LevelSelectScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/LevelSelectScreen.java @@ -99,7 +99,7 @@ private void spawnPlanetBorders() { if (Gdx.input.justTouched()) { dispose(); logger.info("Loading level {}", planet[4]); - game.setScreen(new MainGameScreen(game)); + game.setScreen(new TurretSelectionScreen(game)); } else { Sprite planetBorder = new Sprite(new Texture("planets/planetBorder.png")); batch.draw(planetBorder, planet[0] - 2, planet[1] - 2, planet[2] + 3, planet[3] + 3); diff --git a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java index 9effe3933..6fa9fb93d 100644 --- a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java @@ -54,8 +54,8 @@ public TurretSelectionScreen(GdxGame game) { // Set up the background batch = new SpriteBatch(); - Texture introImage = new Texture(TEXTURE); - introSprite = new Sprite(introImage); + Texture backgroundImage = new Texture(TEXTURE); + introSprite = new Sprite(backgroundImage); introSprite.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); From 338a8035bbd1bb7d4d68b28127750097c6a1ec3f Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 11 Sep 2023 16:28:54 +1000 Subject: [PATCH 72/72] Made the turret adding process more dynaic --- .../game/screens/TurretSelectionScreen.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java index 6fa9fb93d..17cd21319 100644 --- a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java @@ -61,14 +61,8 @@ public TurretSelectionScreen(GdxGame game) { turretList = new ArrayList<>(); // Add turrets to the list - turretList.add(TowerType.WEAPON); - turretList.add(TowerType.TNT); - turretList.add(TowerType.DROID); - turretList.add(TowerType.WALL); - turretList.add(TowerType.FIRE); - turretList.add(TowerType.STUN); - turretList.add(TowerType.INCOME); - + turretList.addAll(Arrays.asList(TowerType.values())); + // Restrictions can be added to the arrays i.e. map == "Forest" && level == 1 Skin skin = new Skin(Gdx.files.internal("flat-earth/skin/flat-earth-ui.json")); @@ -143,6 +137,10 @@ public void render(float delta) { stage.draw(); } + public List getTurretList() { + return turretList; + } + @Override public void dispose() { stage.dispose();