diff --git a/source/core/assets/sounds/waves/wave-end/Wave_Over_01.ogg b/source/core/assets/sounds/waves/wave-end/Wave_Over_01.ogg new file mode 100644 index 000000000..43516a3a1 Binary files /dev/null and b/source/core/assets/sounds/waves/wave-end/Wave_Over_01.ogg differ diff --git a/source/core/assets/sounds/waves/wave-start/Wave_Start_04.ogg b/source/core/assets/sounds/waves/wave-start/Wave_Start_04.ogg new file mode 100644 index 000000000..1437dc9f2 Binary files /dev/null and b/source/core/assets/sounds/waves/wave-start/Wave_Start_04.ogg differ diff --git a/source/core/assets/sounds/waves/wave-start/Wave_Start_06.ogg b/source/core/assets/sounds/waves/wave-start/Wave_Start_06.ogg new file mode 100644 index 000000000..987aa7a8b Binary files /dev/null and b/source/core/assets/sounds/waves/wave-start/Wave_Start_06.ogg differ diff --git a/source/core/assets/sounds/waves/wave-start/Wave_Start_07.ogg b/source/core/assets/sounds/waves/wave-start/Wave_Start_07.ogg new file mode 100644 index 000000000..9da75b33d Binary files /dev/null and b/source/core/assets/sounds/waves/wave-start/Wave_Start_07.ogg differ diff --git a/source/core/assets/sounds/waves/wave-start/Wave_Start_Alarm.ogg b/source/core/assets/sounds/waves/wave-start/Wave_Start_Alarm.ogg new file mode 100644 index 000000000..def03145f Binary files /dev/null and b/source/core/assets/sounds/waves/wave-start/Wave_Start_Alarm.ogg differ 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 3ed44061f..5c054bff5 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -166,7 +166,9 @@ public class ForestGameArea extends GameArea { "sounds/engineers/firing_auto.mp3", "sounds/engineers/firing_single.mp3", "sounds/projectiles/on_collision.mp3", - "sounds/projectiles/explosion.mp3" + "sounds/projectiles/explosion.mp3", + "sounds/waves/wave-start/Wave_Start_Alarm.ogg", + "sounds/waves/wave-end/Wave_Over_01.ogg" }; private static final String backgroundMusic = "sounds/background/Sci-Fi1.ogg"; private static final String[] forestMusic = {backgroundMusic}; @@ -174,6 +176,7 @@ public class ForestGameArea extends GameArea { private final TerrainFactory terrainFactory; private Entity player; + private Entity waves; // Variables to be used with spawn projectile methods. This is the variable // that should occupy the direction param. @@ -192,7 +195,9 @@ public ForestGameArea(TerrainFactory terrainFactory) { this.terrainFactory = terrainFactory; } - // Add this method to start the wave spawning timer when the game starts. + /** + * Add this method to start the wave spawning timer when the game starts. + */ private void startWaveTimer() { waveTimer = new Timer(); waveTimer.scheduleAtFixedRate(new TimerTask() { @@ -203,7 +208,9 @@ public void run() { }, 0, 10000); // 10000 milliseconds = 10 seconds } - // Add this method to stop the wave timer when the game ends or as needed. + /** + * Add this method to stop the wave timer when the game ends or as needed. + */ private void stopWaveTimer() { if (waveTimer != null) { waveTimer.cancel(); @@ -211,6 +218,9 @@ private void stopWaveTimer() { } } + /** + * Cases to spawn a wave + */ private void spawnWave() { wave++; switch (wave) { @@ -255,13 +265,17 @@ public void create() { // Set up infrastructure for end game tracking player = spawnPlayer(); - player.getEvents().addListener("spawnWave", this::spawnWave); + + waves = WaveFactory.createWaves(); + spawnEntity(waves); + waves.getEvents().addListener("spawnWave", this::spawnMob); + playMusic(); - spawnXenoGrunts(); - startWaveTimer(); + //spawnXenoGrunts(); + //startWaveTimer(); spawnScrap(); - spawnDeflectXenoGrunt(15, 5); - spawnSplittingXenoGrunt(15, 4); + //spawnDeflectXenoGrunt(15, 5); + //spawnSplittingXenoGrunt(15, 4); spawnScrap(); spawnTNTTower(); spawnWeaponTower(); @@ -469,17 +483,33 @@ private void spawnProjectile(Vector2 position, short targetLayer, int space, int Entity Projectile = ProjectileFactory.createFireBall(targetLayer, new Vector2(direction, position.y + space), speed); Projectile.setPosition(position); spawnEntity(Projectile); - } - - private void spawnXenoGrunts() { - int[] pickedLanes = random.ints(1, 7) - .distinct().limit(5).toArray(); - for (int i = 0; i < NUM_GRUNTS; i++) { - GridPoint2 randomPos = new GridPoint2(19, pickedLanes[i]); - Entity xenoGrunt = NPCFactory.createXenoGrunt(); - xenoGrunt.setScale(1.5f, 1.5f); - spawnEntityAt(xenoGrunt, randomPos, true, false); + } + + /** + * Spawn an entity on the map. Is called during a wave. Add cases here for each mob type + * @param entity mob to be spawned + * @param randomPos position to be spawned at + */ + public void spawnMob(String entity, GridPoint2 randomPos) { + Entity mob; + switch (entity) { + case "Xeno": + mob = NPCFactory.createXenoGrunt(); + break; + case "SplittingXeno": + mob = NPCFactory.createSplittingXenoGrunt(); + break; + case "DodgingDragon": + mob = NPCFactory.createDodgingDragonKnight(); + break; + case "DeflectXeno": + mob = NPCFactory.createDeflectXenoGrunt(); + break; + default: + mob = NPCFactory.createBaseNPC(); } + mob.setScale(1.5f, 1.5f); + spawnEntityAt(mob, randomPos, true, false); } // * TEMPORARY FOR TESTING 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 46ef5ebc9..1c7616963 100644 --- a/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java +++ b/source/core/src/main/com/csse3200/game/components/TouchAttackComponent.java @@ -151,8 +151,17 @@ private void onCollisionEnd(Fixture me, Fixture other) { } } + /** + * Choose the weapon to use against the given fixture. + * + * If the fixture has been removed (died) return null, else return the weapon to use. + * */ public Weapon chooseWeapon(Fixture other) { - Entity target = ((BodyUserData) other.getBody().getUserData()).entity; + BodyUserData data = ((BodyUserData) other.getBody().getUserData()); + if (data == null) { + return null; + } + Entity target = data.entity; Weapon weapon = null; if (target.getComponent(CombatStatsComponent.class) != null) { weapon = combatStats.getWeapon(target); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/MobWanderTask.java b/source/core/src/main/com/csse3200/game/components/tasks/MobWanderTask.java index 501e60609..99d37afdc 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/MobWanderTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/MobWanderTask.java @@ -65,29 +65,27 @@ public void start() { @Override public void update() { - //Update the position of the mob + // Update the position of the mob mobPosition = owner.getEntity().getPosition(); // If the mob is at zero health, kill the mob, // play the death animation and stop the task - // This method is the idea of Ahmad who very kindly helped - // with section, massive props to him for his help! + // This method is the idea of Ahmad who very kindly helped with section, massive props to him for his help! if (!isDead && owner.getEntity().getComponent(CombatStatsComponent.class).isDead()) { this.owner.getEntity().getEvents().trigger("dieStart"); currentTask.stop(); isDead = true; + ServiceLocator.getWaveService().updateEnemyCount(); } // Check if the mob has finished death animation else if (isDead && owner.getEntity().getComponent(AnimationRenderComponent.class).isFinished()) { - - // Drop scrap at the mobs location for player - // to collect. + // Drop scrap at the mobs location Entity scrap = DropFactory.createScrapDrop(); scrap.setPosition(mobPosition.x,mobPosition.y); ServiceLocator.getEntityService().register(scrap); - // Delete the mob. + // Delete the mob and update count for number of mobs remaining in the wave owner.getEntity().setFlagForDelete(true); } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/NewMobWanderTask.java b/source/core/src/main/com/csse3200/game/components/tasks/NewMobWanderTask.java index 2a6291e66..f8a196a50 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/NewMobWanderTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/NewMobWanderTask.java @@ -89,6 +89,7 @@ else if (isDead && owner.getEntity().getComponent(AnimationRenderComponent.class Entity scrap = DropFactory.createScrapDrop(); scrap.setPosition(mobPosition.x,mobPosition.y); ServiceLocator.getEntityService().register(scrap); + ServiceLocator.getWaveService().updateEnemyCount(); // Delete the mob. owner.getEntity().setFlagForDelete(true); diff --git a/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java b/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java new file mode 100644 index 000000000..0c59b6c59 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java @@ -0,0 +1,124 @@ +package com.csse3200.game.components.tasks.waves; + +import com.badlogic.gdx.math.GridPoint2; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; + +public class LevelWaves extends Entity { + List waves = new ArrayList<>(); + private float spawnDelay; + private GameTime gameTime; + private long startTime; + private Random rand = new Random(); + private int currentRandom = 0; + private int previousRandom = 0; + private int mobIndex; + private int waveIndex; + private int numWaves; + + /** + * Constructor for the LevelWaves class + * @param spawnDelay time to elapse between each wave + */ + public LevelWaves(int spawnDelay) { + this.spawnDelay = spawnDelay; + this.gameTime = ServiceLocator.getTimeSource(); + this.startTime = this.gameTime.getTime(); + this.mobIndex = 0; + this.waveIndex = 0; + this.numWaves = 0; + + long currentTime = ServiceLocator.getTimeSource().getTime(); + // Setting the timestamp for when the next mobs will spawn. + // Currently, the delay of mobs spawning after wave start + // is hardcoded but will fix in the next push. + ServiceLocator.getWaveService().setNextWaveTime(currentTime + 10000); + } + + /** + * Add a wave to the level and increment the total number of waves + * @param wave to be added + */ + public void addWave(WaveClass wave) { + this.numWaves++; + this.waves.add(wave); + } + + /** + * Retrieve a wave at an index in the list + * @param index wave number to be retireved + * @return instance of a wave class + */ + public WaveClass getWave(int index) { + return this.waves.get(index); + } + + /** + * Spawn the wave and all the associated mobs for that wave + */ + public void spawnWave() { + if (gameTime.getTime() >= startTime + spawnDelay * 1000) { + do { + currentRandom = rand.nextInt(1, 7); + } while (currentRandom == previousRandom); + ServiceLocator.getWaveService().setNextLane(currentRandom); + GridPoint2 randomPos = new GridPoint2(19, currentRandom); + this.getEvents().trigger("spawnWave", waves.get(waveIndex) + .getMobs().get(mobIndex), randomPos); + startTime = gameTime.getTime(); + mobIndex++; + previousRandom = currentRandom; + } else if (mobIndex == waves.get(waveIndex).getSize()) { + this.getEvents().trigger("waveFinishedSpawning"); + mobIndex = 0; + } + } + + /** + * Set the wave index + * @param index + */ + public void setWaveIndex(int index) { + this.waveIndex = index; + } + + /** + * Get the total number of waves in this level + * @return number of waves in the level + */ + public int getNumWaves() { + return this.numWaves; + } + + public float getSpawnDelay() { + return this.spawnDelay; + } + + public long getStartTime() { + return this.startTime; + } + + /** + * Get the current mob index + * @return mob index + */ + public int getMobIndex() { + return this.mobIndex; + } + + /** + * Get the current wave index + * @return wave index + */ + public int getWaveIndex() { + return this.waveIndex; + } + +} + diff --git a/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveClass.java b/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveClass.java new file mode 100644 index 000000000..f5d3a9f41 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveClass.java @@ -0,0 +1,60 @@ +package com.csse3200.game.components.tasks.waves; + +import com.badlogic.gdx.math.GridPoint2; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ServiceLocator; + +import java.util.*; + + +public class WaveClass { + private HashMap entities; + private GameTime gameTime; + private long startTime; + private List wave; + private Random rand = new Random(); + private int mobIndex; + + /** + * Constructor for the WaveClass + * @param entities: HashMap of entities and the quantity of them to be spawned in this wave + */ + public WaveClass(HashMap entities) { + this.entities = entities; + this.wave = entitiesToWave(); + this.mobIndex = 0; + } + + /** + * Get the entities that are part of this wave and randomise the order they are spawned + * @return mobs for the wave + */ + private List entitiesToWave() { + List enemies = new ArrayList<>(); + for (Map.Entry set : entities.entrySet()) { + for (int i = 0; i < set.getValue(); i++) { + enemies.add(set.getKey()); + } + } + Collections.shuffle(enemies); + return enemies; + } + + /** + * Gets the current number of entities spawned in the wave + * @return size of the current wave (number of entities) + */ + public int getSize() { + return this.wave.size(); + } + + /** + * Gets the current entities that have spawned in the wave + * @return list of mobs in the current wave + */ + public List getMobs() { + return this.wave; + } + +} diff --git a/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveTask.java b/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveTask.java new file mode 100644 index 000000000..5821e6e01 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/tasks/waves/WaveTask.java @@ -0,0 +1,140 @@ +package com.csse3200.game.components.tasks.waves; + +import com.badlogic.gdx.audio.Music; +import com.badlogic.gdx.audio.Sound; +import com.csse3200.game.ai.tasks.DefaultTask; +import com.csse3200.game.ai.tasks.PriorityTask; +import com.csse3200.game.ai.tasks.Task; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public class WaveTask extends DefaultTask implements PriorityTask { + private static final Logger logger = LoggerFactory.getLogger(WaveTask.class); + private LevelWaves level; + private WaveClass currentWave; + private final GameTime globalTime; + private int currentWaveIndex = 0; + private boolean waveInProgress; + private float startTime = 0; + private float endTime = 0; + private final float INITIAL_WAIT_INTERVAL = 10; + private final int SPAWNING_INTERVAL = 10; + + private static final String[] waveSounds = { + "sounds/waves/wave-start/Wave_Start_Alarm.ogg", + "sounds/waves/wave-end/Wave_Over_01.ogg" + }; + + private final Sound waveStart; + private final Sound waveEnd; + + /** + * Constructor for the WaveTask + */ + public WaveTask() { + this.globalTime = ServiceLocator.getTimeSource(); + this.waveInProgress = false; + loadSounds(); + this.waveStart = ServiceLocator.getResourceService().getAsset(waveSounds[0], Sound.class); + this.waveEnd = ServiceLocator.getResourceService().getAsset(waveSounds[1], Sound.class); + } + + /** + * Load the sounds to be played when a wave starts or ends + */ + public void loadSounds() { + ResourceService resourceService = ServiceLocator.getResourceService(); + resourceService.loadSounds(waveSounds); + } + + /** + * Get the sounds to be played when a wave starts or ends + * @return String array of sounds + */ + public String[] getSounds() { + return waveSounds; + } + + /** + * Gets the priority of the current task + * @return priority of the WaveTask + */ + @Override + public int getPriority() { + return 10; // High priority task + } + + /** + * Starts the WaveTask and initialises all relevant attributes. + * Sets the current count of enemies to be the size of the current wave. + */ + @Override + public void start() { + super.start(); + this.owner.getEntity().getEvents().addListener("waveFinishedSpawning", () -> waveInProgress = false); + this.waveInProgress = true; + this.level = (LevelWaves) this.owner.getEntity(); + this.currentWave = level.getWave(currentWaveIndex); + ServiceLocator.getWaveService().setEnemyCount(currentWave.getSize()); + logger.info("Wave {} starting with {} enemies", currentWaveIndex, ServiceLocator.getWaveService().getEnemyCount()); + this.waveStart.play(); + // endTime = globalTime.getTime() + (SPAWNING_INTERVAL * 1000); + } + + /** + * Checks if the current wave has finished (i.e. number of mobs left is 0) and calls the next wave + * to begin. If there are still mobs remaining, continue the wave. + */ + @Override + public void update() { + if (ServiceLocator.getWaveService().getEnemyCount() == 0) { + currentWaveIndex++; + + long currentTime = ServiceLocator.getTimeSource().getTime(); + // Setting the timestamp for when the next mobs will spawn. + // Currently, the delay of mobs spawning after wave start + // is hardcoded but will fix in the next push. + ServiceLocator.getWaveService().setNextWaveTime(currentTime + 10000); + + // Check if level has been completed - no more waves remaining + if (currentWaveIndex == this.level.getNumWaves()) { + logger.info("No waves remaining, level completed"); + ServiceLocator.getWaveService().setLevelCompleted(); + + } else { + // Spawn the next wave + logger.info("No enemies remaining, begin next wave"); + this.waveEnd.play(); + this.waveInProgress = true; + this.level.setWaveIndex(currentWaveIndex); + // Set the service wave count to the current wave index. + ServiceLocator.getWaveService().setWaveCount(currentWaveIndex); + this.currentWave = this.level.getWave(currentWaveIndex); + ServiceLocator.getWaveService().setEnemyCount(currentWave.getSize()); + //endTime = globalTime.getTime() + (SPAWNING_INTERVAL * 1000L); // reset end time + } + + } else { + logger.info("{} enemies remaining in wave {}", ServiceLocator.getWaveService().getEnemyCount(), currentWaveIndex); + logger.info("WAVE SERVICE NUMBER: Wave Number {}",ServiceLocator.getWaveService().getWaveCount()); + logger.info("NEXT WAVE AT {}", ServiceLocator.getWaveService().getNextWaveTime()); + logger.info("TIME IS {}", ServiceLocator.getTimeSource().getTime()); + if (waveInProgress) { + this.level.spawnWave(); + } + } + } + + /** + * Checks if the current wave is in progress + * @return true if the wave is in progress, false otherwise + */ + public boolean isWaveInProgress() { + return waveInProgress; + } + } diff --git a/source/core/src/main/com/csse3200/game/entities/PredefinedWeapons.java b/source/core/src/main/com/csse3200/game/entities/PredefinedWeapons.java index 78dfffdd2..8e20a6781 100644 --- a/source/core/src/main/com/csse3200/game/entities/PredefinedWeapons.java +++ b/source/core/src/main/com/csse3200/game/entities/PredefinedWeapons.java @@ -1,6 +1,7 @@ package com.csse3200.game.entities; import com.csse3200.game.entities.configs.ProjectileConfig; +import com.csse3200.game.files.FileLoader; public class PredefinedWeapons { // Melee attacks @@ -9,11 +10,8 @@ public class PredefinedWeapons { public static Melee axe = new Melee(9, 3, "fire", 1, 1); public static Melee kick = new Melee(2, 1, "earth", 1, 1); + //TODO import defined projectiles for mobs public static ProjectileConfig fireBall = new ProjectileConfig(); public static ProjectileConfig frostBall = new ProjectileConfig(); - // Projectile attacks TODO: change Weapon and Melee to Projectile class -// public static Weapon fireBall = new Melee(9, 20, "fire", 1, 1); -// public static Weapon frostBall = new Melee(6, 20, "ice", 1, 1); -// public static Weapon hurricane = new Melee(7, 20, "air", 1, 1); } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java index d77ff1d1d..9686e829d 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java @@ -6,7 +6,7 @@ import com.csse3200.game.components.player.InventoryComponent; import com.csse3200.game.components.player.PlayerActions; import com.csse3200.game.components.player.PlayerStatsDisplay; -import com.csse3200.game.components.tasks.SpawnWaveTask; +import com.csse3200.game.components.tasks.waves.WaveTask; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.configs.PlayerConfig; import com.csse3200.game.files.FileLoader; @@ -36,9 +36,6 @@ public class PlayerFactory { public static Entity createPlayer() { InputComponent inputComponent = ServiceLocator.getInputService().getInputFactory().createForPlayer(); - AITaskComponent aiComponent = - new AITaskComponent() - .addTask(new SpawnWaveTask()); Entity player = new Entity() .addComponent(new TextureRenderComponent("images/box_boy_leaf.png")) @@ -50,7 +47,6 @@ public static Entity createPlayer() { .addComponent(new CombatStatsComponent(1000, 5000)) .addComponent(new InventoryComponent(stats.gold)) .addComponent(inputComponent) - .addComponent(aiComponent) .addComponent(new PlayerStatsDisplay()); PhysicsUtils.setScaledCollider(player, 0.6f, 0.3f); diff --git a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java new file mode 100644 index 000000000..d059a6efb --- /dev/null +++ b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java @@ -0,0 +1,39 @@ +package com.csse3200.game.entities.factories; + +import com.csse3200.game.ai.tasks.AITaskComponent; +import com.csse3200.game.components.tasks.waves.LevelWaves; +import com.csse3200.game.components.tasks.waves.WaveClass; +import com.csse3200.game.components.tasks.waves.WaveTask; +import com.csse3200.game.entities.Entity; + +import java.util.HashMap; + + +public class WaveFactory { + /** + * Create a Wave entity. + * Each wave class represents a single wave, then they are appended to a level. + * Cases can be written in here to set what happens for each level. + * @return entity + */ + public static Entity createWaves() { + HashMap mobs = new HashMap<>(); + mobs.put("Xeno", 3); + mobs.put("DodgingDragon", 4); + HashMap mobs2 = new HashMap<>(); + mobs2.put("Xeno", 3); + WaveClass wave1 = new WaveClass(mobs); + WaveClass wave2 = new WaveClass(mobs2); + LevelWaves level = new LevelWaves(10); + level.addWave(wave1); + level.addWave(wave2); + AITaskComponent aiComponent = + new AITaskComponent() + .addTask(new WaveTask()); + return level.addComponent(aiComponent); + } + + private WaveFactory() { + throw new IllegalStateException("Instantiating static util class"); + } +} diff --git a/source/core/src/main/com/csse3200/game/screens/MainGameScreen.java b/source/core/src/main/com/csse3200/game/screens/MainGameScreen.java index 4cadb1db3..0ca778809 100644 --- a/source/core/src/main/com/csse3200/game/screens/MainGameScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/MainGameScreen.java @@ -90,6 +90,7 @@ public MainGameScreen(GdxGame game) { ServiceLocator.registerEntityService(new EntityService()); ServiceLocator.registerRenderService(new RenderService()); ServiceLocator.registerGameEndService(new GameEndService()); + ServiceLocator.registerWaveService(new WaveService()); renderer = RenderFactory.createRenderer(); renderer.getCamera().getEntity().setPosition(CAMERA_POSITION); diff --git a/source/core/src/main/com/csse3200/game/services/ServiceLocator.java b/source/core/src/main/com/csse3200/game/services/ServiceLocator.java index 5683715e4..12d53a83f 100644 --- a/source/core/src/main/com/csse3200/game/services/ServiceLocator.java +++ b/source/core/src/main/com/csse3200/game/services/ServiceLocator.java @@ -25,6 +25,7 @@ public class ServiceLocator { private static InputService inputService; private static ResourceService resourceService; private static GameEndService gameEndService; + private static WaveService waveService; public static CurrencyService getCurrencyService() { return currencyService; @@ -58,6 +59,8 @@ public static GameEndService getGameEndService() { return gameEndService; } + public static WaveService getWaveService() { return waveService; } + public static void registerCurrencyService(CurrencyService service) { logger.debug("Registering currency service {}", service); currencyService = service; @@ -97,6 +100,10 @@ public static void registerGameEndService(GameEndService source) { logger.debug("Registering game end service service {}", source); gameEndService = source; } + public static void registerWaveService(WaveService source) { + logger.debug("Registering wave service {}", source); + waveService = source; + } public static void clear() { entityService = null; diff --git a/source/core/src/main/com/csse3200/game/services/WaveService.java b/source/core/src/main/com/csse3200/game/services/WaveService.java new file mode 100644 index 000000000..cd26acf8b --- /dev/null +++ b/source/core/src/main/com/csse3200/game/services/WaveService.java @@ -0,0 +1,119 @@ +package com.csse3200.game.services; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WaveService { + private static final Logger logger = LoggerFactory.getLogger(WaveService.class); + private int enemyCount; + private boolean gameOver = false; + private int lane; + + private int waveCount = 1; + + private boolean levelCompleted = false; + + private long nextWaveTime; + + + /** + * Constructor for the Wave Service + */ + public WaveService() { + this.enemyCount = 0; + } + + /** + * Set the enemy limit. During instantiation, limit defaults to 0. + * @param newLimit as an integer representing the maximum number of enemy deaths + */ + public void setEnemyCount(int newLimit) { + if (newLimit > 0) { + enemyCount = newLimit; + } + } + + /** + * Returns the number of enemy left + * @return (int) enemy count + */ + + public int getEnemyCount() { + return enemyCount; + } + + /** + * Updates enemy count + * If enemy count is 0, the game is over. + */ + public void updateEnemyCount() { + enemyCount -= 1; + logger.info("{} enemies remaining in wave", getEnemyCount()); + } + + /** + * Set the level to be completed. Will be called when there are no waves remaining. + */ + public void setLevelCompleted() { + if (!levelCompleted) { + levelCompleted = true; + } + } + + /** + * Sets the waveCount + * @param lane as an integer representing the next lane of a mob. + */ + public void setNextLane(int lane) { + this.lane = lane; + } + + /** + * Returns the next lane number of a mob + * @return (int) lane number + */ + public int getNextLane() { + return lane; + } + + /** + * Returns the game over state + * @return (boolean) true if the game is over; false otherwise + */ + public boolean isLevelCompleted() { + return levelCompleted; + } + + /** + * Returns the game over state + * @return (boolean) true if the game is over; false otherwise + */ + public int getWaveCount() { + return this.waveCount; + } + + /** + * Sets the waveCount + * @param waveCount as an integer representing the current wave number. + * This will be added to the current wave number. + */ + public void setWaveCount(int waveCount) { + this.waveCount += waveCount; + } + + /** + * Returns time of the next wave. + * @return (long) A timestamp of when the next mobs will spawn. Used for UI elements. + */ + public long getNextWaveTime() { + return this.nextWaveTime; + } + + /** + * Sets the next wave timestamp + * @param nextWaveTime as a long which is the time when then next mobs will spawn. + */ + public void setNextWaveTime(long nextWaveTime) { + this.nextWaveTime = nextWaveTime; + } +} diff --git a/source/core/src/test/com/csse3200/game/components/tasks/waves/LevelWavesTest.java b/source/core/src/test/com/csse3200/game/components/tasks/waves/LevelWavesTest.java new file mode 100644 index 000000000..da0f54eff --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/waves/LevelWavesTest.java @@ -0,0 +1,38 @@ +package com.csse3200.game.components.tasks.waves; + +import com.csse3200.game.entities.factories.WaveFactory; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.services.ServiceLocator; +import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +@ExtendWith(GameExtension.class) +@ExtendWith(MockitoExtension.class) +class LevelWavesTest { + + LevelWaves levelWaves; + WaveClass wave; + ServiceLocator serviceLocator; + + @BeforeEach + void setUp() { + levelWaves = (LevelWaves) WaveFactory.createWaves(); + wave = mock(WaveClass.class); + } + + @Test + public void testAddWave() { + levelWaves.addWave(wave); + assertEquals(3, levelWaves.waves.size()); + } + + @Test + public void testSpawnWaveStart() { + + } +} diff --git a/source/core/src/test/com/csse3200/game/components/tasks/waves/WaveClassTest.java b/source/core/src/test/com/csse3200/game/components/tasks/waves/WaveClassTest.java new file mode 100644 index 000000000..e811e68ef --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/waves/WaveClassTest.java @@ -0,0 +1,4 @@ +package com.csse3200.game.components.tasks.waves; + +class WaveClassTest { +} diff --git a/source/core/src/test/com/csse3200/game/components/tasks/waves/WaveTaskTest.java b/source/core/src/test/com/csse3200/game/components/tasks/waves/WaveTaskTest.java new file mode 100644 index 000000000..0926c9e9b --- /dev/null +++ b/source/core/src/test/com/csse3200/game/components/tasks/waves/WaveTaskTest.java @@ -0,0 +1,60 @@ +package com.csse3200.game.components.tasks.waves; + +import com.badlogic.gdx.audio.Sound; +import com.csse3200.game.areas.ForestGameArea; +import com.csse3200.game.areas.terrain.TerrainFactory; +import com.csse3200.game.components.tasks.DroidCombatTask; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.services.GameTime; +import com.csse3200.game.services.ResourceService; +import com.csse3200.game.services.ServiceLocator; +import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.logging.Level; + +import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(GameExtension.class) +@ExtendWith(MockitoExtension.class) +class WaveTaskTest { + + WaveTask waveTask; + ResourceService resourceService; + LevelWaves level; + ForestGameArea gameArea; + @BeforeEach + void setUp() { + resourceService = ServiceLocator.getResourceService(); + GameTime globalTime = mock(GameTime.class); + level = mock(LevelWaves.class); + ServiceLocator.registerTimeSource(globalTime); + waveTask = new WaveTask(); + } + + @Test + public void testLoadSounds() { + String[] sounds = waveTask.getSounds(); + resourceService.getAsset(sounds[0], Sound.class); + resourceService.getAsset(sounds[1], Sound.class); + } + + @Test + public void testGetPriority() { + int priority = waveTask.getPriority(); + assertEquals(10, priority); + } + + @Test + public void testStartWave() { + waveTask.start(); + assertEquals(1, waveTask.getPriority()); + assertTrue(waveTask.isWaveInProgress()); + } + +} diff --git a/source/core/src/test/com/csse3200/game/entities/factories/WaveFactoryTest.java b/source/core/src/test/com/csse3200/game/entities/factories/WaveFactoryTest.java new file mode 100644 index 000000000..d154e1a6b --- /dev/null +++ b/source/core/src/test/com/csse3200/game/entities/factories/WaveFactoryTest.java @@ -0,0 +1,16 @@ +package com.csse3200.game.entities.factories; + +import com.csse3200.game.extensions.GameExtension; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(GameExtension.class) +@ExtendWith(MockitoExtension.class) +class WaveFactoryTest { + + @BeforeEach + void setUp() { + + } +}