diff --git a/source/core/src/main/com/csse3200/game/ai/tasks/AITaskComponent.java b/source/core/src/main/com/csse3200/game/ai/tasks/AITaskComponent.java index b54745014..7c165ef09 100644 --- a/source/core/src/main/com/csse3200/game/ai/tasks/AITaskComponent.java +++ b/source/core/src/main/com/csse3200/game/ai/tasks/AITaskComponent.java @@ -1,7 +1,6 @@ package com.csse3200.game.ai.tasks; import com.csse3200.game.components.Component; -import com.csse3200.game.components.ComponentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 30ada73cc..31876af2e 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -8,11 +8,14 @@ import com.csse3200.game.components.ProjectileEffects; +import com.csse3200.game.services.GameTime; import com.csse3200.game.utils.math.RandomUtils; import com.csse3200.game.services.ResourceService; import com.csse3200.game.services.ServiceLocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import java.security.SecureRandom; import java.util.Timer; import static com.csse3200.game.screens.AssetLoader.loadAllAssets; @@ -21,6 +24,13 @@ public class ForestGameArea extends GameArea { private static final Logger logger = LoggerFactory.getLogger(ForestGameArea.class); + /* Change below to change the number of ms between spawns of engineers in the case */ + private static final long ENGINEER_MIN_SPAWN_INTERVAL = 1000; + + private long lastSpawnTime = 0; + + private int wave = 0; + private Timer waveTimer; private static final GridPoint2 PLAYER_SPAWN = new GridPoint2(2, 4); // Temporary spawn point for testing @@ -212,8 +222,6 @@ public class ForestGameArea extends GameArea { private static final String BACKGROUND_MUSIC = "sounds/background/Sci-Fi1.ogg"; private static final String[] forestMusic = {BACKGROUND_MUSIC}; - private Entity player; - private Entity waves; /** * Initialise this ForestGameArea to use the provided TerrainFactory. @@ -223,19 +231,6 @@ public ForestGameArea() { super(); } - /** - * Add this method to start the wave spawning timer when the game starts. - */ -// private void startWaveTimer() { -// waveTimer = new Timer(); -// waveTimer.scheduleAtFixedRate(new TimerTask() { -// @Override -// public void run() { -// spawnWave(); -// } -// }, 0, 10000); // 10000 milliseconds = 10 seconds -// } - /** * Add this method to stop the wave timer when the game ends or as needed. */ @@ -246,37 +241,6 @@ private void stopWaveTimer() { } } - /** - * Cases to spawn a wave - */ -// private void spawnWave() { -// wave++; -// switch (wave) { -// case 1: -// case 2: -// spawnFireWorm(); -// spawnDragonKnight(); -// -// break; -// case 3: -// spawnSkeleton(); -// spawnWizard(); -// // mobBoss2 = spawnMobBoss2(); -// break; -// case 4: -// spawnWaterQueen(); -// spawnWaterSlime(); -// // mobBoss2 = spawnMobBoss2(); -// -// break; -// case 5: -// spawnDemonBoss(); -// default: -// // Handle other wave scenarios if needed -// break; -// } -// } - /** * Create the game area, including terrain, static entities (trees), dynamic entities (player) */ @@ -291,15 +255,14 @@ public void create() { spawnTerrain(); // Set up infrastructure for end game tracking - // player = spawnPlayer(); - logger.info("Creating waves"); - waves = WaveFactory.createWaves(); + Entity waves = WaveFactory.createWaves(); spawnEntity(waves); waves.getEvents().addListener("spawnWave", this::spawnMob); spawnScrap(); spawnGapScanners(); + } private void displayUI() { @@ -345,19 +308,6 @@ private void spawnTerrain() { } - //private Entity spawnPlayer() { - // Entity newPlayer = PlayerFactory.createPlayer(); - // spawnEntityAt(newPlayer, PLAYER_SPAWN, true, true); - // return newPlayer; - // } - - // Spawn player at a specific position - // private Entity spawnPlayer(GridPoint2 position) { - // Entity newPlayer = PlayerFactory.createPlayer(); -// spawnEntityAt(newPlayer, position, true, true); - // return newPlayer; - // } - /** * Spawn an entity on the map. Is called during a wave. Add cases here for each mob type * @param entity mob to be spawned @@ -478,10 +428,19 @@ private void spawnScrap() { * and trigger engineer spawning */ private void spawnGapScanners() { + GameTime gameTime = ServiceLocator.getTimeSource(); + long currSpawnTime = gameTime.getTime(); + + long diff = currSpawnTime - this.lastSpawnTime; + if (diff < ENGINEER_MIN_SPAWN_INTERVAL) { + return; + } + for (int i = 0; i < terrain.getMapBounds(0).y; i++) { Entity scanner = GapScannerFactory.createScanner(); spawnEntityAt(scanner, new GridPoint2(0, i), true, true); } + this.lastSpawnTime = currSpawnTime; } diff --git a/source/core/src/main/com/csse3200/game/areas/terrain/TerrainComponent.java b/source/core/src/main/com/csse3200/game/areas/terrain/TerrainComponent.java index c6c67b642..e4706c022 100644 --- a/source/core/src/main/com/csse3200/game/areas/terrain/TerrainComponent.java +++ b/source/core/src/main/com/csse3200/game/areas/terrain/TerrainComponent.java @@ -16,8 +16,6 @@ import com.csse3200.game.services.CurrencyService; import com.csse3200.game.services.ResourceService; import com.csse3200.game.services.ServiceLocator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Render a tiled terrain for a given tiled map and orientation. A terrain is a map of tiles that 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 4fb249d6d..d5bcf90af 100644 --- a/source/core/src/main/com/csse3200/game/components/EffectsComponent.java +++ b/source/core/src/main/com/csse3200/game/components/EffectsComponent.java @@ -22,7 +22,6 @@ public class EffectsComponent extends Component { private final boolean aoe; private HitboxComponent hitboxComponent; private final short targetLayer; - private Array burnEntities = new Array<>(); private ArrayList stunnedEntities = new ArrayList<>(); /** diff --git a/source/core/src/main/com/csse3200/game/components/gamearea/EngineerCountDisplay.java b/source/core/src/main/com/csse3200/game/components/gamearea/EngineerCountDisplay.java index 09d2d3d73..42510a607 100644 --- a/source/core/src/main/com/csse3200/game/components/gamearea/EngineerCountDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/gamearea/EngineerCountDisplay.java @@ -61,7 +61,7 @@ private void addActors() { // Animate the engineer count label engineerTb.setPosition(table.getX() - 200f, Gdx.graphics.getHeight() - 145f); - engineerTb.addAction(new SequenceAction(Actions.moveTo(table.getX() + 20f, Gdx.graphics.getHeight() - 145, + engineerTb.addAction(new SequenceAction(Actions.moveTo(table.getX() + 20f, Gdx.graphics.getHeight() - 145f, 1f, Interpolation.fastSlow))); } diff --git a/source/core/src/main/com/csse3200/game/components/gamearea/GameAreaDisplay.java b/source/core/src/main/com/csse3200/game/components/gamearea/GameAreaDisplay.java index 9a8149869..deac53d83 100644 --- a/source/core/src/main/com/csse3200/game/components/gamearea/GameAreaDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/gamearea/GameAreaDisplay.java @@ -14,7 +14,6 @@ * Displays the name of the current game area. */ public class GameAreaDisplay extends UIComponent { - private static final Logger logger = LoggerFactory.getLogger(GameAreaDisplay.class); private static final String DEFAULT_STYLE = "default"; private String gameAreaName = ""; private Label title; @@ -27,7 +26,6 @@ public GameAreaDisplay(String gameAreaName) { public void create() { super.create(); addActors(); - final Skin skin = new Skin(); } public void render(float delta) { diff --git a/source/core/src/main/com/csse3200/game/components/maingame/LevelProgressBar.java b/source/core/src/main/com/csse3200/game/components/maingame/LevelProgressBar.java index d885beb87..44171b44a 100644 --- a/source/core/src/main/com/csse3200/game/components/maingame/LevelProgressBar.java +++ b/source/core/src/main/com/csse3200/game/components/maingame/LevelProgressBar.java @@ -1,6 +1,5 @@ package com.csse3200.game.components.maingame; -import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; diff --git a/source/core/src/main/com/csse3200/game/components/npc/DodgingComponent.java b/source/core/src/main/com/csse3200/game/components/npc/DodgingComponent.java index f07028878..26aadd4d4 100644 --- a/source/core/src/main/com/csse3200/game/components/npc/DodgingComponent.java +++ b/source/core/src/main/com/csse3200/game/components/npc/DodgingComponent.java @@ -1,5 +1,6 @@ package com.csse3200.game.components.npc; +import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.csse3200.game.components.Component; import com.csse3200.game.physics.PhysicsEngine; @@ -32,7 +33,6 @@ public class DodgingComponent extends Component { private float dodgeSpeed = 1.75f; private float originalSpeed; // Original entity vertical speed private PhysicsEngine physics; - private Random random = new Random(); // Sometimes the raycast mechanic doesn't detect the other entity because of the // target's (or self) collider size does not match. This value makes sure the @@ -95,7 +95,7 @@ public void create() { * @param mobPos The current Vector2 mob position in the map. */ public void changeTraverseDirection(Vector2 mobPos) { - int randDirection = random.nextInt(2) == 1 ? -1 : 1; + int randDirection = MathUtils.random(0,2) == 1 ? -1 : 1; if (isTargetVisible(mobPos)) { // If mob is in the top half quadrant of the map grid, make the entity dodge diff --git a/source/core/src/main/com/csse3200/game/components/npc/EngineerMenuComponent.java b/source/core/src/main/com/csse3200/game/components/npc/EngineerMenuComponent.java deleted file mode 100644 index 096fb5962..000000000 --- a/source/core/src/main/com/csse3200/game/components/npc/EngineerMenuComponent.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.csse3200.game.components.npc; - -import com.badlogic.gdx.graphics.Camera; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.BitmapFont; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.scenes.scene2d.InputEvent; -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.scenes.scene2d.utils.Drawable; -import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; -import com.badlogic.gdx.utils.Array; -import com.csse3200.game.components.player.HumanAnimationController; -import com.csse3200.game.components.tower.TowerUpgraderComponent; -import com.csse3200.game.entities.Entity; -import com.csse3200.game.entities.EntityService; -import com.csse3200.game.physics.PhysicsLayer; -import com.csse3200.game.input.EngineerInputComponent; -import com.csse3200.game.rendering.AnimationRenderComponent; -import com.csse3200.game.services.ServiceLocator; -import com.csse3200.game.ui.UIComponent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class EngineerMenuComponent extends UIComponent { - private Logger logger = LoggerFactory.getLogger(EngineerMenuComponent.class); - Table table; - - @Override - public void create() { - super.create(); - } - - @Override - public void draw(SpriteBatch batch) { - // draw is handled by the stage - } - - /** - * Creates a menu for the engineer - * @param x cursor x coordinate - * @param y cursor y coordinate - * @param camera camera of the game - */ - public void createMenu(float x, float y, Camera camera) { - this.table = createTable(x, y, camera); - - // add buttons - TextButton moveButton = createButton("Move"); - TextButton repairButton = createButton("Repair"); - - // add listeners to buttons - AnimationRenderComponent animator = getEntity().getComponent(AnimationRenderComponent.class); - HumanAnimationController controller = getEntity().getComponent(HumanAnimationController.class); - EngineerInputComponent input = ServiceLocator.getInputService().getEngineerInput(); - - moveButton.addListener(new ClickListener() { - @Override - public void clicked(InputEvent event, float x, float y) { - input.setMoveClicked(true); - controller.deselectEngineer(animator.getCurrentAnimation()); - - //logger.info("Move button clicked"); - } - }); - - repairButton.addListener(new ClickListener() { - @Override - public void clicked(InputEvent event, float x, float y) { - controller.deselectEngineer(animator.getCurrentAnimation()); - EntityService entityService = ServiceLocator.getEntityService(); - Array tower = entityService.getEntitiesInLayer(getEntity(), 0.1f, PhysicsLayer.TOWER); - if (tower.size == 0) { - logger.info("No tower to repair"); - return; - } - logger.info("repairing"); - tower.get(0).getComponent(TowerUpgraderComponent.class).repairTower(); - //logger.info("Repair button clicked"); - } - }); - - table.add(moveButton).grow(); - table.row(); - table.add(repairButton).grow(); - table.row(); - stage.addActor(table); - - } - - /** - * Creates a table for the menu - * @param x cursor x coordinate - * @param y cursor y coordinate - * @param camera camera of the game - * @return table for the menu - */ - private Table createTable(float x, float y, Camera camera) { - Table table = new Table(); - table.top(); - table.defaults().pad(0).space(0); - table.setSize(90, 60); // fixed table size - - // convert cursor position to stage coordinates - Vector3 entityCoordinates = new Vector3(x, y, 0); - Vector3 entityScreenCoordinate = camera.project(entityCoordinates); - Vector2 stageCoordinates = stage.screenToStageCoordinates( - new Vector2(entityScreenCoordinate.x, entityScreenCoordinate.y)); - stage.getViewport().unproject(stageCoordinates); - table.setPosition(stageCoordinates.x, stageCoordinates.y); - - // set table background - String imageFilePath = "images/ui/Sprites/UI_Glass_Frame_Standard_01a.png"; - Drawable drawable = new TextureRegionDrawable(new TextureRegion(new Texture(imageFilePath))); - table.setBackground(drawable); - - return table; - } - - /** - * Creates a button for the menu - * @param text text to be displayed on the button - * @return the button - */ - private TextButton createButton(String text) { - String upImageFilePath = "images/ui/Sprites/UI_Glass_Button_Medium_Lock_01a2.png"; - String downImageFilePath = "images/ui/Sprites/UI_Glass_Button_Medium_Press_01a2.png"; - - Drawable upDrawable = new TextureRegionDrawable(new TextureRegion(new Texture(upImageFilePath))); - Drawable downDrawable = new TextureRegionDrawable(new TextureRegion(new Texture(downImageFilePath))); - TextButton button = new TextButton(text, - new TextButton.TextButtonStyle(upDrawable, downDrawable, null, new BitmapFont())); - - button.setTransform(true); - return button; - } - - /** - * Removes the menu from the stage - */ - public void removeMenu() { - table.clear(); - table.remove(); - } - -} diff --git a/source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java b/source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java index bcaf10f1f..5317c6b4b 100644 --- a/source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/player/HumanAnimationController.java @@ -2,7 +2,6 @@ import com.badlogic.gdx.audio.Sound; import com.csse3200.game.components.Component; -import com.csse3200.game.components.npc.EngineerMenuComponent; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; @@ -108,7 +107,6 @@ void animateLeftWalk() { } else { animator.startAnimation(WALKL_ANIM); } -// runSound.play(); } /** @@ -206,18 +204,4 @@ public boolean isClicked() { public void setClicked(boolean clicked) { this.clicked = clicked; } - - /** - * Deseelects the engineer entity by starting the appropriate animation without an outline - * and removes the engineer menu from the stage - * @param currentAnimation the current animation of the entity - */ - public void deselectEngineer(String currentAnimation) { - AnimationRenderComponent animator = this.entity.getComponent(AnimationRenderComponent.class); - EngineerMenuComponent menu = this.entity.getComponent(EngineerMenuComponent.class); - - animator.startAnimation(currentAnimation.substring(0, currentAnimation.lastIndexOf('_'))); - menu.removeMenu(); - setClicked(false); - } } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java b/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java index bd150803f..92ee1f3a9 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/MobTask/MobTask.java @@ -129,18 +129,19 @@ public void update() { if(mob.getCenterPosition().x <= 1) { mob.getComponent(CombatStatsComponent.class).setHealth(0); - ServiceLocator.getGameEndService().updateEngineerCount(); + } // death check if ((mob.getComponent(CombatStatsComponent.class).getHealth() <= 0 && !deathFlag)) { // decrement engineer count // ! tests failing because of textbox - + ServiceLocator.getGameEndService().updateEngineerCount(); changeState(State.DEATH); animate(); movementTask.stop(); deathFlag = true; + } else if (deathFlag && animation.isFinished()) { ServiceLocator.getWaveService().updateEnemyCount(); @@ -312,17 +313,14 @@ private void dropCurrency() { float randomValue = MathUtils.random(0f,1f); logger.info("Random value: " + randomValue); Entity currency; - if (randomValue <= CRYSTAL_DROP_RATE) { - currency = DropFactory.createCrystalDrop(); + if (randomValue <= SCRAP_DROP_RATE && randomValue > CRYSTAL_DROP_RATE) { + currency = DropFactory.createScrapDrop(); currency.setPosition(mob.getPosition().x,mob.getPosition().y); ServiceLocator.getEntityService().register(currency); - - } - else if (randomValue <= SCRAP_DROP_RATE) { - currency = DropFactory.createScrapDrop(); + } else if (randomValue <= CRYSTAL_DROP_RATE) { + currency = DropFactory.createCrystalDrop(); currency.setPosition(mob.getPosition().x,mob.getPosition().y); ServiceLocator.getEntityService().register(currency); - } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java b/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java index 6b4410a1d..39270ec91 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/bosstask/IceBabyTask.java @@ -39,8 +39,8 @@ public class IceBabyTask extends DefaultTask implements PriorityTask { private Vector2 currentPos; private Vector2 walkPos; private MovementTask walkTask; - private static int xRightBoundary = 17; - private static int xLeftBoundary = 12; + private int xRightBoundary = 17; + private int xLeftBoundary = 12; private boolean startFlag = false; private boolean isWalking; /** Animation constants */ 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 3598a245a..f4a803893 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 @@ -10,6 +10,8 @@ import com.csse3200.game.physics.raycast.RaycastHit; import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; @@ -32,7 +34,7 @@ public class EngineerCombatTask extends DefaultTask implements PriorityTask { // The Engineer's attributes. private final float maxRange; // The maximum range of the Engineer's weapon. // weaponCapacity is the number of shots fired before the engineer has to reload - private static final int weaponCapacity = 10; + private static final int WEAPON_CAPACITY = 10; private int shotsFired = 0; // Tracks the number of shots fired in the current cycle private final Vector2 maxRangePosition = new Vector2(); @@ -41,6 +43,8 @@ public class EngineerCombatTask extends DefaultTask implements PriorityTask { private long endTime; private long reloadTime; + private static final Logger logger = LoggerFactory.getLogger(EngineerCombatTask.class); + private ArrayList hits = new ArrayList<>(); private final RaycastHit hit = new RaycastHit(); private ArrayList targets = new ArrayList<>(); @@ -106,7 +110,7 @@ public void updateEngineerState() { owner.getEntity().getEvents().trigger(IDLE_RIGHT); engineerState = STATE.IDLE_RIGHT; } else { - if (shotsFired <= weaponCapacity) { + if (shotsFired <= WEAPON_CAPACITY) { owner.getEntity().getEvents().trigger(FIRING); owner.getEntity().getEvents().trigger(ENGINEER_PROJECTILE_FIRED); // this might be changed to an event which gets triggered everytime the tower enters the firing state @@ -166,6 +170,7 @@ public boolean isTargetVisible() { // If there is an obstacle in the path to the max range point, mobs visible. Vector2 position = owner.getEntity().getCenterPosition(); hits.clear(); + targets.clear(); for (int i = 5; i > -5; i--) { if (physics.raycast(position, new Vector2(position.x + maxRange, position.y + i), TARGET, hit)) { hits.add(hit); @@ -182,16 +187,17 @@ public boolean isTargetVisible() { */ public Vector2 fetchTarget() { // Initial nearest position for comparison - int lowest = 10; + float currentClosest = Float.MAX_VALUE; Vector2 nearest = new Vector2(owner.getEntity().getCenterPosition().x, owner.getEntity().getCenterPosition().y); + Vector2 enggPosition = owner.getEntity().getCenterPosition(); - // Find the nearest target from the array of targets - for (Vector2 tgt : targets){ - if (Math.abs(tgt.y - nearest.y) < lowest) { - lowest = (int)Math.abs(tgt.y - nearest.y); - nearest = tgt; + for (Vector2 target : targets) { + float distance = enggPosition.dst(target); // euclidean distance + if (distance < currentClosest) { + currentClosest = distance; + nearest = target; } } return nearest; 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 421fbf6ce..f6b055873 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 @@ -124,14 +124,14 @@ private void doEngineerThings() { float engY = owner.getEntity().getCenterPosition().y; float targetY = combatTask.fetchTarget().y; // if the engineer is positioned within the tolerance range of the mob's y position, enter combat state - if (engY < targetY + TOLERANCE && + if (engY < targetY + TOLERANCE && engY > targetY - TOLERANCE) { startCombat(); // move into position for targeting mob } else if (!this.isSelected()) { - Vector2 newPos = new Vector2(owner.getEntity().getPosition().x, combatTask.fetchTarget().y); - startMoving(newPos); + Vector2 newPos = new Vector2(owner.getEntity().getPosition().x, combatTask.fetchTarget().y); + startMoving(newPos); } } } diff --git a/source/core/src/main/com/csse3200/game/components/tasks/scanner/ScannerTask.java b/source/core/src/main/com/csse3200/game/components/tasks/scanner/ScannerTask.java index 3405cecd0..f04b7f2c9 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/scanner/ScannerTask.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/scanner/ScannerTask.java @@ -8,6 +8,7 @@ import com.csse3200.game.physics.PhysicsEngine; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.raycast.RaycastHit; +import com.csse3200.game.services.GameEndService; import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; @@ -31,8 +32,8 @@ public class ScannerTask extends DefaultTask implements PriorityTask { private boolean mobs = false; // track the number of engineers spawned. - private static final int maxEngineers = 3; - private int engineerCount = 0; + private static final int MAX_ENGINEERS = ServiceLocator.getGameEndService().getEngineerCount(); + /** * ScannerTask Constructor @@ -68,12 +69,12 @@ public void update() { scan(); if (!towers && !engineers && mobs) { // spawn engineers now - if (engineerCount < maxEngineers) { + if (ServiceLocator.getGameEndService().getNumSpawnedEngineers() < MAX_ENGINEERS) { Entity engineer = EngineerFactory.createEngineer(); engineer.setPosition(new Vector2((int)(selfPosition.x + 1),(int) selfPosition.y)); ServiceLocator.getEntityService().register(engineer); - engineerCount += 1; + ServiceLocator.getGameEndService().incrementNumSpawnedEngineers(); } } endTime = timeSource.getTime() + SCAN_INTERVAL; diff --git a/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java b/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java index cca65b2e5..19d6e910c 100644 --- a/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java +++ b/source/core/src/main/com/csse3200/game/components/tasks/waves/LevelWaves.java @@ -1,6 +1,7 @@ package com.csse3200.game.components.tasks.waves; import com.badlogic.gdx.math.GridPoint2; +import com.badlogic.gdx.math.MathUtils; import com.csse3200.game.entities.Entity; import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; @@ -14,7 +15,6 @@ public class LevelWaves extends Entity { 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; @@ -63,7 +63,7 @@ public WaveClass getWave(int index) { public void spawnWave() { if (gameTime.getTime() >= startTime + spawnDelay * 1000) { do { - currentRandom = rand.nextInt(0, ServiceLocator.getMapService().getHeight()); + currentRandom = MathUtils.random(0, ServiceLocator.getMapService().getHeight()); } while (currentRandom == previousRandom); ServiceLocator.getWaveService().setNextLane(currentRandom); GridPoint2 randomPos = new GridPoint2(19, currentRandom); diff --git a/source/core/src/main/com/csse3200/game/components/tower/EconTowerAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/EconTowerAnimationController.java index 3e9a3a785..dd6a1a07f 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/EconTowerAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/tower/EconTowerAnimationController.java @@ -17,10 +17,10 @@ public class EconTowerAnimationController extends Component { private static final String ECO_MOVE = "move1"; private static final String ECO_IDLE = "idle"; - private static final String PING = "sounds/towers/eco_tower_ping.mp3"; + private static final String PING_PATH = "sounds/towers/eco_tower_ping.mp3"; private final Sound ping = ServiceLocator.getResourceService().getAsset( - PING, Sound.class); + PING_PATH, Sound.class); AnimationRenderComponent animator; diff --git a/source/core/src/main/com/csse3200/game/components/tower/TNTAnimationController.java b/source/core/src/main/com/csse3200/game/components/tower/TNTAnimationController.java index 4ebfa2bb8..595bc6930 100644 --- a/source/core/src/main/com/csse3200/game/components/tower/TNTAnimationController.java +++ b/source/core/src/main/com/csse3200/game/components/tower/TNTAnimationController.java @@ -11,10 +11,10 @@ */ public class TNTAnimationController extends Component { private AnimationRenderComponent animator; - private static final String EXPLOSION = "sounds/towers/explosion.mp3"; + private static final String EXPLOSION_PATH = "sounds/towers/explosion.mp3"; private final Sound explosion = ServiceLocator.getResourceService().getAsset( - EXPLOSION, Sound.class); + EXPLOSION_PATH, Sound.class); /** * Creation call for a TNTAnimationController, fetches the animationRenderComponent that this controller will diff --git a/source/core/src/main/com/csse3200/game/currency/Currency.java b/source/core/src/main/com/csse3200/game/currency/Currency.java index 81820ad5f..d3d974a26 100644 --- a/source/core/src/main/com/csse3200/game/currency/Currency.java +++ b/source/core/src/main/com/csse3200/game/currency/Currency.java @@ -4,7 +4,6 @@ import org.slf4j.LoggerFactory; public abstract class Currency { - private static final Logger logger = LoggerFactory.getLogger(Currency.class); // The logo of the currency @@ -53,7 +52,7 @@ public String getTexture() { * @param addedAmount The amount the currency will be incremented by. */ public void modify(int addedAmount) { - logger.debug("Modifying " + this.getClass().getSimpleName() + "by " + addedAmount); + logger.debug(String.format("Modifying %s by %d", this.getClass().getSimpleName(), addedAmount)); this.amount += addedAmount; } diff --git a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfig.java b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfig.java index 121fb0b9c..37ab5489e 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfig.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfig.java @@ -1,6 +1,6 @@ package com.csse3200.game.entities.configs; /** Defines the basic set of properties for an Engineer entity to be loaded by EngineerFactory */ -public class EngineerConfig extends BaseEntityConfig { +public class EngineerConfig { public int health = 1; public int baseAttack = 0; } \ No newline at end of file diff --git a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java index 21113b7a0..5e6bacdbf 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/EngineerConfigs.java @@ -3,7 +3,7 @@ /** * Defines the properties stored in Engineer config files to be loaded by the Engineer Factory. */ -public class EngineerConfigs extends BaseEntityConfig { +public class EngineerConfigs { public BaseEntityConfig engineer = new BaseEntityConfig(); public int health = 10; public int baseAttack = 1; diff --git a/source/core/src/main/com/csse3200/game/entities/configs/MobBossConfigs.java b/source/core/src/main/com/csse3200/game/entities/configs/MobBossConfigs.java index 1ae0fc4af..602fbab59 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/MobBossConfigs.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/MobBossConfigs.java @@ -4,5 +4,4 @@ * Defines the properties stored in mob boss config files to be loaded by the Mob Boss Factory. */ public class MobBossConfigs extends BaseEntityConfig { - public int baseAttack = 0; } diff --git a/source/core/src/main/com/csse3200/game/entities/configs/ProjectileConfig.java b/source/core/src/main/com/csse3200/game/entities/configs/ProjectileConfig.java index 757129687..925864cac 100644 --- a/source/core/src/main/com/csse3200/game/entities/configs/ProjectileConfig.java +++ b/source/core/src/main/com/csse3200/game/entities/configs/ProjectileConfig.java @@ -6,8 +6,6 @@ * Configuration for projectiles. */ public class ProjectileConfig extends BaseEntityConfig implements Weapon { - public int health = 1; - public int baseAttack = 0; public int getDamage() { return baseAttack; diff --git a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java index 593c12de4..55188a494 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/EngineerFactory.java @@ -6,7 +6,6 @@ import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.EffectComponent; import com.csse3200.game.components.TouchAttackComponent; -import com.csse3200.game.components.npc.EngineerMenuComponent; import com.csse3200.game.components.player.HumanAnimationController; import com.csse3200.game.components.tasks.human.HumanWanderTask; import com.csse3200.game.entities.Entity; @@ -59,7 +58,6 @@ public static Entity createEngineer() { .addComponent(new CombatStatsComponent(config.health, config.baseAttack)) .addComponent(animator) .addComponent(new HumanAnimationController()) - .addComponent(new EngineerMenuComponent()) .addComponent(aiComponent); engineer.getComponent(AITaskComponent.class).addTask(new HumanWanderTask(COMBAT_TASK_PRIORITY, ENGINEER_RANGE)); diff --git a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java index 0b9f6db73..2bcd6af06 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/WaveFactory.java @@ -1,5 +1,6 @@ package com.csse3200.game.entities.factories; +import com.badlogic.gdx.math.MathUtils; import com.csse3200.game.ai.tasks.AITaskComponent; import com.csse3200.game.components.tasks.waves.LevelWaves; import com.csse3200.game.components.tasks.waves.WaveClass; @@ -15,70 +16,73 @@ import java.util.HashMap; import java.util.Random; - 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 - */ + // CONSTANTS + private static final String SKELETON = "Skeleton"; + private static final String COAT = "Coat"; + private static final String WATER_QUEEN = "WaterQueen"; + private static final String SPLITTING_WATER_SLIME = "SplittingWaterSlime"; + private static final String ARCANE_ARCHER = "ArcaneArcher"; + private static final String WIZARD = "Wizard"; + private static final String SPLITTING_NIGHT_BORNE = "SplittingNightBorne"; + private static final String DRAGON_KNIGHT = "DragonKnight"; + private static final String NECROMANCER = "Necromancer"; + private static final String SPLITTING_ROCKY = "SplittingRocky"; + private static final String FIRE_WORM = "FireWorm"; + private static final String DODGING_DRAGON = "DodgingDragon"; + private static final String DEFLECT_FIRE_WIZARD = "DeflectFireWizard"; + private static final String ICE_BOSS = "IceBoss"; + private static final String PATRICK_BOSS = "PatrickBoss"; + private static final String FIRE_BOSS = "FireBoss"; private static final Logger logger = LoggerFactory.getLogger(WaveFactory.class); - private static Random rand = new Random(); private static final ArrayList MELEE_MOBS = new ArrayList<>(Arrays.asList( - "Skeleton", "Coat", "DragonKnight", "Necromancer" + SKELETON, COAT, DRAGON_KNIGHT, NECROMANCER )); - private static final ArrayList> lvl1Structure = new ArrayList<>(Arrays.asList( - new ArrayList<>(Arrays.asList("Coat" - )), new ArrayList<>(Arrays.asList("Coat", "WaterQueen" - )), new ArrayList<>(Arrays.asList("WaterQueen", "SplittingWaterSlime" - )), new ArrayList<>(Arrays.asList("Coat", "WaterQueen", "SplittingWaterSlime" - )) + private static final ArrayList> LVL1_STRUCTURE = new ArrayList<>(Arrays.asList( + new ArrayList<>(Arrays.asList(COAT + )), new ArrayList<>(Arrays.asList(COAT, WATER_QUEEN + )), new ArrayList<>(Arrays.asList(WATER_QUEEN, SPLITTING_WATER_SLIME + )), new ArrayList<>(Arrays.asList(COAT, WATER_QUEEN, SPLITTING_WATER_SLIME + )) )); - private static final ArrayList> lvl2Structure = new ArrayList<>(Arrays.asList( - new ArrayList<>(Arrays.asList("Skeleton" - )), new ArrayList<>(Arrays.asList("Skeleton", "ArcaneArcher" - )), new ArrayList<>(Arrays.asList("Skeleton", "Wizard" - )), new ArrayList<>(Arrays.asList("Skeleton", "SplittingNightBorne" - )), new ArrayList<>(Arrays.asList("Wizard", "SplittingNightBorne" - )), new ArrayList<>(Arrays.asList("SplittingNightBorne", "Skeleton" - )), new ArrayList<>(Arrays.asList("Wizard", "SplittingNightBorne" - )), new ArrayList<>(Arrays.asList("ArcaneArcher", "SplittingNightBorne", "Wizard" - )), new ArrayList<>(Arrays.asList("Skeleton", "ArcaneArcher", "Wizard", "SplittingNightBorne" - )) + private static final ArrayList> LVL2_STRUCTURE = new ArrayList<>(Arrays.asList( + new ArrayList<>(Arrays.asList(SKELETON + )), new ArrayList<>(Arrays.asList(SKELETON, ARCANE_ARCHER + )), new ArrayList<>(Arrays.asList(SKELETON, WIZARD + )), new ArrayList<>(Arrays.asList(SKELETON, SPLITTING_NIGHT_BORNE + )), new ArrayList<>(Arrays.asList(WIZARD, SPLITTING_NIGHT_BORNE + )), new ArrayList<>(Arrays.asList(SPLITTING_NIGHT_BORNE, SKELETON + )), new ArrayList<>(Arrays.asList(WIZARD, SPLITTING_NIGHT_BORNE + )), new ArrayList<>(Arrays.asList(ARCANE_ARCHER, SPLITTING_NIGHT_BORNE, WIZARD + )), new ArrayList<>(Arrays.asList(SKELETON, ARCANE_ARCHER, WIZARD, SPLITTING_NIGHT_BORNE + )) )); - private static final ArrayList> lvl3Structure = new ArrayList<>(Arrays.asList( - new ArrayList<>(Arrays.asList("Necromancer" - )), new ArrayList<>(Arrays.asList("Necromancer", "DodgingDragon" - )), new ArrayList<>(Arrays.asList("Necromancer", "FireWorm" - )), new ArrayList<>(Arrays.asList("Necromancer", "DeflectFireWizard" - )), new ArrayList<>(Arrays.asList("DeflectFireWizard", "FireWorm" - )), new ArrayList<>(Arrays.asList("DodgingDragon", "FireWorm" - )), new ArrayList<>(Arrays.asList("DodgingDragon", "Necromancer" - )), new ArrayList<>(Arrays.asList("FireWorm", "Necromancer" - )), new ArrayList<>(Arrays.asList("DeflectFireWizard", "Necromancer" - )), new ArrayList<>(Arrays.asList("DodgingDragon", "DeflectFireWizard", "Necromancer" - )), new ArrayList<>(Arrays.asList("FireWorm", "Necromancer", "DodgingDragon" - )), new ArrayList<>(Arrays.asList("FireWorm", "SplittingRocky", "Necromancer" - )), new ArrayList<>(Arrays.asList("SplittingRocky", "DeflectFireWizard", "FireWorm" - )), new ArrayList<>(Arrays.asList("DeflectFireWizard", "SplittingRocky", "Necromancer", "DodgingDragon", "FireWorm" - )) + private static final ArrayList> LVL3_STRUCTURE = new ArrayList<>(Arrays.asList( + new ArrayList<>(Arrays.asList(NECROMANCER + )), new ArrayList<>(Arrays.asList(NECROMANCER, DODGING_DRAGON + )), new ArrayList<>(Arrays.asList(NECROMANCER, FIRE_WORM + )), new ArrayList<>(Arrays.asList(NECROMANCER, DEFLECT_FIRE_WIZARD + )), new ArrayList<>(Arrays.asList(DEFLECT_FIRE_WIZARD, FIRE_WORM + )), new ArrayList<>(Arrays.asList(DODGING_DRAGON, FIRE_WORM + )), new ArrayList<>(Arrays.asList(DODGING_DRAGON, NECROMANCER + )), new ArrayList<>(Arrays.asList(FIRE_WORM, NECROMANCER + )), new ArrayList<>(Arrays.asList(DEFLECT_FIRE_WIZARD, NECROMANCER + )), new ArrayList<>(Arrays.asList(DODGING_DRAGON, DEFLECT_FIRE_WIZARD, NECROMANCER + )), new ArrayList<>(Arrays.asList(FIRE_WORM, NECROMANCER, DODGING_DRAGON + )), new ArrayList<>(Arrays.asList(FIRE_WORM, SPLITTING_ROCKY, NECROMANCER + )), new ArrayList<>(Arrays.asList(SPLITTING_ROCKY, DEFLECT_FIRE_WIZARD, FIRE_WORM + )), new ArrayList<>(Arrays.asList(DEFLECT_FIRE_WIZARD, SPLITTING_ROCKY, NECROMANCER, DODGING_DRAGON, FIRE_WORM + )) )); - private static final String BOSS_1 = "IceBoss"; - private static final String BOSS_2 = "PatrickBoss"; - private static final String BOSS_3 = "FireBoss"; - /** * The function will create the waves depending on the level selected by the user. - * */ + */ public static Entity createWaves() { - int chosenLevel = GameLevelData.getSelectedLevel(); int difficulty; switch (chosenLevel) { @@ -93,9 +97,7 @@ public static Entity createWaves() { } LevelWaves level = createLevel(difficulty); - AITaskComponent aiComponent = - new AITaskComponent() - .addTask(new WaveTask()); + AITaskComponent aiComponent = new AITaskComponent().addTask(new WaveTask()); return level.addComponent(aiComponent); } @@ -136,21 +138,21 @@ public static LevelWaves createLevel(int chosenLevel) { switch (chosenLevel) { case 2: - boss = BOSS_2; + boss = PATRICK_BOSS; bossHealth = LVL2_BOSS_BASE_HEALTH; - possibleMobs = lvl2Structure; + possibleMobs = LVL2_STRUCTURE; minMobs = 6; break; case 3: - boss = BOSS_3; + boss = FIRE_BOSS; bossHealth = LVL3_BOSS_BASE_HEALTH; - possibleMobs = lvl3Structure; + possibleMobs = LVL3_STRUCTURE; minMobs = 8; break; default: - boss = BOSS_1; + boss = ICE_BOSS; bossHealth = LVL1_BOSS_BASE_HEALTH; - possibleMobs = lvl1Structure; + possibleMobs = LVL1_STRUCTURE; minMobs = 5; break; } @@ -171,17 +173,16 @@ public static LevelWaves createLevel(int chosenLevel) { if (leftToSort == 0) { num = minMobs - currentMobs; } else { - num = rand.nextInt(minMobs - currentMobs - (2 * leftToSort) - 2) + 2; + num = MathUtils.random(minMobs - currentMobs - (2 * leftToSort) - 2) + 2; + System.out.println(num + " for " + mob + " at wave " + atWave); currentMobs += num; } // Calculate the health - final int RANGE_BASE_HEALTH = 60; - int health = RANGE_BASE_HEALTH; + int health = 60; if (MELEE_MOBS.contains(mob)) { // The base health for the different mobs - final int MELEE_BASE_HEALTH = 80; - health = MELEE_BASE_HEALTH; + health = 80; } int[] mobStats = {num, health + (atWave * chosenLevel)}; mobs.put(mob, mobStats); @@ -208,3 +209,4 @@ private WaveFactory() { throw new IllegalStateException("Instantiating static util class"); } } + diff --git a/source/core/src/main/com/csse3200/game/input/DropInputComponent.java b/source/core/src/main/com/csse3200/game/input/DropInputComponent.java index df9ed2146..6ea786348 100644 --- a/source/core/src/main/com/csse3200/game/input/DropInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/DropInputComponent.java @@ -3,27 +3,21 @@ import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; -import com.csse3200.game.areas.ForestGameArea; import com.csse3200.game.components.npc.DropComponent; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.EntityService; import com.csse3200.game.services.ServiceLocator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Objects; public class DropInputComponent extends InputComponent { - private static final Logger logger = LoggerFactory.getLogger(ForestGameArea.class); private final EntityService entityService; private final Camera camera; - int value; /** * Constructor for the DropInputComponent * @param camera the camera to be used, this is the camera that the game is rendered with */ public DropInputComponent(Camera camera) { - this.value = ServiceLocator.getCurrencyService().getScrap().getAmount(); this.entityService = ServiceLocator.getEntityService(); this.camera = camera; } @@ -45,15 +39,14 @@ public Camera getCamera() { * @param screenY The y coordinate, origin is in the upper left corner * @param pointer the pointer for the event. * @param button the button - * @return + * @return true if the event was handled; false otherwise */ @Override public boolean touchDown(int screenX, int screenY, int pointer, int button) { - Vector3 worldCoordinates = new Vector3((float) screenX , (float) screenY, 0); + Vector3 worldCoordinates = new Vector3(screenX , screenY, 0); getCamera().unproject(worldCoordinates); // translate from screen to world coordinates Vector2 cursorPosition = new Vector2(worldCoordinates.x, worldCoordinates.y); Entity clickedEntity = entityService.getEntityAtPosition(cursorPosition.x, cursorPosition.y); - //logger.info("Clicked entity: " + clickedEntity); if (clickedEntity != null && clickedEntity.getComponent(DropComponent.class) != null) { int value = clickedEntity.getComponent(DropComponent.class).getValue(); @@ -69,15 +62,13 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { ServiceLocator.getCurrencyService().getDisplay().updateCrystalsStats(); } - float X = clickedEntity.getCenterPosition().x; - float Y = clickedEntity.getCenterPosition().y; + float xValue = clickedEntity.getCenterPosition().x; + float yValue = clickedEntity.getCenterPosition().y; // remove the entity from the game EntityService.removeEntity(clickedEntity); // display a visual indication that currency has been picked up - ServiceLocator.getCurrencyService().getDisplay().currencyPopUp(X, Y, value, 10); - - //logger.info("Scrap amount: " + ServiceLocator.getCurrencyService().getScrap().getAmount()); + ServiceLocator.getCurrencyService().getDisplay().currencyPopUp(xValue, yValue, value, 10); return true; } return false; diff --git a/source/core/src/main/com/csse3200/game/input/EngineerInputComponent.java b/source/core/src/main/com/csse3200/game/input/EngineerInputComponent.java index 4b2388878..75a37bb07 100644 --- a/source/core/src/main/com/csse3200/game/input/EngineerInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/EngineerInputComponent.java @@ -6,15 +6,10 @@ import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.csse3200.game.ai.tasks.AITaskComponent; -import com.csse3200.game.ai.tasks.PriorityTask; -import com.csse3200.game.ai.tasks.Task; -import com.csse3200.game.components.npc.EngineerMenuComponent; import com.csse3200.game.components.player.HumanAnimationController; -import com.csse3200.game.components.tasks.human.HumanMovementTask; import com.csse3200.game.components.tasks.human.HumanWanderTask; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.EntityService; -import com.csse3200.game.entities.factories.EngineerFactory; import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; @@ -28,8 +23,9 @@ public class EngineerInputComponent extends InputComponent { private Camera camera; private EntityService entityService; - private Entity selectedEngineer = null; - private boolean moveClicked = false; + public Entity selectedEngineer = null; + + private final String OUTLINE_STRING = "_outline"; public EngineerInputComponent(Game game, Camera camera) { this.game = game; @@ -37,8 +33,9 @@ public EngineerInputComponent(Game game, Camera camera) { this.entityService = ServiceLocator.getEntityService(); } + @Override public boolean touchDown(int screenX, int screenY, int pointer, int button) { - Vector3 worldCoordinates = new Vector3((float) screenX , (float) screenY, 0); + Vector3 worldCoordinates = new Vector3(screenX , screenY, 0); camera.unproject(worldCoordinates); Vector2 cursorPosition = new Vector2(worldCoordinates.x, worldCoordinates.y); camera.project(worldCoordinates); @@ -47,43 +44,17 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { // Case when engineer is not clicked if (engineer == null || engineer.getComponent(HumanAnimationController.class) == null) { - if (selectedEngineer != null && moveClicked) { + if (selectedEngineer != null) { + // Clicked a tile with an engineer selected and clicked on not an engineer moveEngineer(cursorPosition); - selectedEngineer = null; - moveClicked = false; return true; } else { + // Clicked a tile with no engineer selected or engineer on the tile return false; } } // Case when engineer is clicked - AnimationRenderComponent animator = engineer.getComponent(AnimationRenderComponent.class); - String currentAnimation = animator.getCurrentAnimation(); - HumanAnimationController controller = engineer.getComponent(HumanAnimationController.class); - EngineerMenuComponent menu = engineer.getComponent(EngineerMenuComponent.class); - - if (engineer.equals(selectedEngineer)) { - // Deselect the engineer by clicking on itself - this.getWanderTask().setSelected(false); - selectedEngineer = null; - moveClicked = false; - if (currentAnimation.contains("_outline")) { - controller.deselectEngineer(currentAnimation); - //logger.info("Engineer deselected"); - } - } else { - this.selectedEngineer = engineer; - this.getWanderTask().setSelected(true); - moveClicked = false; - logger.info("Engineer size: {}", engineer.getScale()); - - // outline image if it is not already outlined and vice versa - if (!currentAnimation.contains("_outline")) { - animator.startAnimation(currentAnimation + "_outline"); - menu.createMenu(cursorPosition.x, cursorPosition.y, camera); - controller.setClicked(true); - } - } + switchEngineer(engineer); return true; } @@ -106,12 +77,65 @@ private void manualShoot() { wander.startCombat(); } + /** + * Switches the specified engineer + * If the given engineer is already selected, deselect it + * If another engineer is selected, deselect it and select the new given engineer + * @param engineer (Entity) the specified engineer + */ + private void switchEngineer(Entity engineer) { + if (engineer.equals(this.selectedEngineer)) { + this.getWanderTask().setSelected(false); + this.selectedEngineer = null; + switchOutline(engineer); + } + else if (selectedEngineer == null) { + this.selectedEngineer = engineer; + switchOutline(engineer); + this.getWanderTask().setSelected(true); + + } else { + this.getWanderTask().setSelected(false); + switchOutline(this.selectedEngineer); + switchOutline(engineer); + this.selectedEngineer = engineer; + this.getWanderTask().setSelected(true); + + } + } + + /** + * Switches the outline of the given engineer and deselects/selects engineer as needed + * If outlined -> remove outline, deselect engineer and vice versa + * @param engineer (Entity) the specified engineer + */ + private void switchOutline(Entity engineer) { + AnimationRenderComponent animator = engineer.getComponent(AnimationRenderComponent.class); + String currentAnimation = animator.getCurrentAnimation(); + HumanAnimationController controller = engineer.getComponent(HumanAnimationController.class); + if (currentAnimation.contains(OUTLINE_STRING)) { + animator.startAnimation(currentAnimation.substring(0, currentAnimation.lastIndexOf('_'))); + controller.setClicked(false); + } else { + animator.startAnimation(currentAnimation + OUTLINE_STRING); + controller.setClicked(true); + } + } + + /** + * Returns the wander task of the selected engineer + * @return (HumanWanderTask) the wander task of the selected engineer + */ private HumanWanderTask getWanderTask() { AITaskComponent movementTask = selectedEngineer.getComponent(AITaskComponent.class); return movementTask.getTask(HumanWanderTask.class); } - private void moveEngineer(Vector2 cursorPosition) { + /** + * Moves the selected engineer to the given cursor position + * @param cursorPosition (Vector2) the cursor position + */ + public void moveEngineer(Vector2 cursorPosition) { if (selectedEngineer == null) { logger.info("Trying to move an engineer that is not selected"); } @@ -123,9 +147,4 @@ private void moveEngineer(Vector2 cursorPosition) { Vector2 dest = cursorPosition.add(offset); wander.startMoving(dest); } - - public void setMoveClicked(boolean moveClicked) { - this.moveClicked = moveClicked; - } - } diff --git a/source/core/src/main/com/csse3200/game/input/UpgradeUIComponent.java b/source/core/src/main/com/csse3200/game/input/UpgradeUIComponent.java index 857a14919..f58199c4a 100644 --- a/source/core/src/main/com/csse3200/game/input/UpgradeUIComponent.java +++ b/source/core/src/main/com/csse3200/game/input/UpgradeUIComponent.java @@ -158,7 +158,7 @@ private Table createUpgradeTable(Entity turretEntity) { upgradeTable.top().left(); upgradeTable.defaults().pad(0).space(0); upgradeTable.setSize(60, 60); - upgradeTable.padTop(30f).padLeft(5f); + upgradeTable.padTop(85f).padLeft(5f); upgradeTable.setPosition(0, round((float) Gdx.graphics.getHeight() / 1.3f)); // The inner table contains the upgrade buttons and the stats display diff --git a/source/core/src/main/com/csse3200/game/screens/HelpScreen/HelpGameDisplay.java b/source/core/src/main/com/csse3200/game/screens/HelpScreen/HelpGameDisplay.java index 0e07a42b5..647db9cf0 100644 --- a/source/core/src/main/com/csse3200/game/screens/HelpScreen/HelpGameDisplay.java +++ b/source/core/src/main/com/csse3200/game/screens/HelpScreen/HelpGameDisplay.java @@ -147,9 +147,9 @@ private void addActors() { TextButton pauseBtn = ButtonFactory.createButton("Back"); // Starting animation for pause button - pauseBtn.setPosition(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() - 150); - pauseBtn.addAction(new SequenceAction(Actions.moveTo(Gdx.graphics.getWidth() - 200, - Gdx.graphics.getHeight() - 150, 1f, Interpolation.fastSlow))); + pauseBtn.setPosition(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() - 150f); + pauseBtn.addAction(new SequenceAction(Actions.moveTo(Gdx.graphics.getWidth() - 200f, + Gdx.graphics.getHeight() - 150f, 1f, Interpolation.fastSlow))); // Spawns a pause menu when the button is pressed. pauseBtn.addListener( diff --git a/source/core/src/main/com/csse3200/game/screens/HelpScreen/TutorialForestGameArea.java b/source/core/src/main/com/csse3200/game/screens/HelpScreen/TutorialForestGameArea.java index e52c593a8..0891acd6a 100644 --- a/source/core/src/main/com/csse3200/game/screens/HelpScreen/TutorialForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/screens/HelpScreen/TutorialForestGameArea.java @@ -139,8 +139,6 @@ public void create() { spawnTerrain(); // Set up infrastructure for end game tracking -// player = spawnPlayer(); - waves = WaveFactory.createWaves(); spawnEntity(waves); waves.getEvents().addListener("spawnWave", this::spawnMob); 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 a6e5fb2a8..99f4977b5 100644 --- a/source/core/src/main/com/csse3200/game/screens/LevelSelectScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/LevelSelectScreen.java @@ -25,10 +25,10 @@ import com.csse3200.game.ui.ButtonFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import static com.badlogic.gdx.scenes.scene2d.ui.Table.Debug.table; import static com.csse3200.game.ui.UIComponent.getSkin; + /** * Represents the level selection screen of the game, where players can choose a planet to play on. * The screen displays different types of planets representing various game levels. @@ -58,6 +58,7 @@ public class LevelSelectScreen extends ScreenAdapter { private Sprite background; private final Music music; + // Stores a time to determine the frame of the planet float timeCounter = 0; private static final String BG_PATH = "planets/background.png"; @@ -185,8 +186,11 @@ private void spawnPlanet(int width, int height, int posx, int posy, String plane batch.draw(planetSprite, posx, posy, width, height); } + /** - * Spawns planet borders and handles interactions with them. + * Spawns the borders of the planets. If a planet is clicked it will load the level + * based on the planet. If a planet is hovered over it will display a border around + * the planet. */ private void spawnPlanetBorders() { Vector3 mousePos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0); @@ -350,6 +354,7 @@ public void render(float delta) { * @param width The new width of the screen. * @param height The new height of the screen. */ + @Override public void resize(int width, int height) { stage.getViewport().update(width, height, true); } diff --git a/source/core/src/main/com/csse3200/game/screens/LoadingScreen.java b/source/core/src/main/com/csse3200/game/screens/LoadingScreen.java index ff5baa60c..ac66819f4 100644 --- a/source/core/src/main/com/csse3200/game/screens/LoadingScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/LoadingScreen.java @@ -44,7 +44,7 @@ public LoadingScreen(GdxGame game) { stage = new Stage(new ScreenViewport()); Skin skin = new Skin(Gdx.files.internal("uiskin.json")); // Use your own skin file loadingLabel = new Label("Loading", skin); - loadingLabel.setPosition(Gdx.graphics.getWidth() / 2 - 50, Gdx.graphics.getHeight() / 2); + loadingLabel.setPosition((float) Gdx.graphics.getWidth() / 2 - 50f, (float) Gdx.graphics.getHeight() / 2); stage.addActor(loadingLabel); } diff --git a/source/core/src/main/com/csse3200/game/services/GameEndService.java b/source/core/src/main/com/csse3200/game/services/GameEndService.java index 8be490699..50e886c31 100644 --- a/source/core/src/main/com/csse3200/game/services/GameEndService.java +++ b/source/core/src/main/com/csse3200/game/services/GameEndService.java @@ -4,7 +4,10 @@ public class GameEndService { - private int engineerCount; + private int remainingEngineerCount; + + private int numSpawnedEngineers = 0; + private boolean gameOver = false; private static final int STARTING_COUNT = 5; private final EngineerCountDisplay display; @@ -13,7 +16,7 @@ public class GameEndService { * Constructor for the Game End Service */ public GameEndService() { - this.engineerCount = STARTING_COUNT; + this.remainingEngineerCount = STARTING_COUNT; this.display = new EngineerCountDisplay(); } @@ -23,7 +26,7 @@ public GameEndService() { */ public void setEngineerCount(int newLimit) { if (newLimit > 0 && newLimit < 1000) { - engineerCount = newLimit; + remainingEngineerCount = newLimit; display.updateCount(); } } @@ -34,7 +37,7 @@ public void setEngineerCount(int newLimit) { */ public int getEngineerCount() { - return engineerCount; + return remainingEngineerCount; } /** @@ -42,10 +45,10 @@ public int getEngineerCount() { * If engineer count is 0, the game is over. */ public void updateEngineerCount() { - engineerCount -= 1; + remainingEngineerCount -= 1; display.updateCount(); - if (engineerCount == 0) { + if (remainingEngineerCount == 0) { gameOver = true; } } @@ -72,4 +75,19 @@ public boolean hasGameEnded() { public EngineerCountDisplay getDisplay() { return display; } + + /** + * Returns the number of spawned engineers + * @return (int) number of spawned engineers + */ + public int getNumSpawnedEngineers() { + return numSpawnedEngineers; + } + + /** + * Increments the number of spawned engineers + */ + public void incrementNumSpawnedEngineers(){ + numSpawnedEngineers += 1; + } } diff --git a/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java b/source/core/src/test/com/csse3200/game/components/player/HumanAnimationControllerTest.java index 1f8f063d6..1647cd9ef 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 @@ -112,4 +112,40 @@ void shouldAnimateDeath() { assertEquals("death", engineer.getComponent(AnimationRenderComponent.class).getCurrentAnimation(), "'deathStart' event should trigger 'death' animation'"); } + + @Test + void notSelectedOnStart() { + assertFalse(engineer.getComponent(HumanAnimationController.class).isClicked(), + "Engineer should not be selected when initialised"); + } + + @Test + void shouldSelectEngineer() { + engineer.getComponent(HumanAnimationController.class).setClicked(true); + AnimationRenderComponent animator = engineer.getComponent(AnimationRenderComponent.class); + + engineer.getEvents().trigger("idleRight"); + assertEquals("idle_right_outline", animator.getCurrentAnimation(), + "After selection, outlined animation should be playing - idle right"); + + engineer.getEvents().trigger("walkLeftStart"); + assertEquals("walk_left_outline", animator.getCurrentAnimation(), + "After selection, outlined animation should be playing - walk left"); + + engineer.getEvents().trigger("walkRightStart"); + assertEquals("walk_right_outline", animator.getCurrentAnimation(), + "After selection, outlined animation should be playing - walk right"); + + engineer.getEvents().trigger("firingSingleStart"); + assertEquals("firing_single_outline", animator.getCurrentAnimation(), + "After selection, outlined animation should be playing - firing single"); + + engineer.getEvents().trigger("hitStart"); + assertEquals("hit_outline", animator.getCurrentAnimation(), + "After selection, outlined animation should be playing - hit"); + + engineer.getEvents().trigger("deathStart"); + assertEquals("death_outline", animator.getCurrentAnimation(), + "After selection, outlined animation should be playing - death"); + } } \ No newline at end of file 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 deleted file mode 100644 index a47359ffc..000000000 --- a/source/core/src/test/com/csse3200/game/components/tasks/waves/WaveTaskTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.csse3200.game.components.tasks.waves; - -import com.badlogic.gdx.audio.Sound; -import com.csse3200.game.ai.tasks.PriorityTask; -import com.csse3200.game.ai.tasks.Task; -import com.csse3200.game.areas.ForestGameArea; -import com.csse3200.game.areas.terrain.TerrainFactory; -import com.csse3200.game.components.tasks.DroidCombatTask; -import com.csse3200.game.components.tasks.waves.LevelWaves; -import com.csse3200.game.components.tasks.waves.WaveTask; -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.jupiter.api.Disabled; -import org.junit.jupiter.api.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; -@Disabled -@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 testStartFirstWave() { - waveTask.start(); - assertEquals(10, waveTask.getPriority()); - assertTrue(waveTask.isWaveInProgress()); - assertEquals(1, waveTask.getCurrentWaveIndex()); - } - - @Test - public void testIsWaveInProgress() { - waveTask.start(); - assertTrue(waveTask.isWaveInProgress()); - } - - @Test - public void testUpdateOnEmptyWaveAndNoNextWave() { - waveTask.start(); - ServiceLocator.getWaveService().setEnemyCount(0); - waveTask.update(); - assertTrue(ServiceLocator.getWaveService().isLevelCompleted()); - } - - @Test - public void testUpdateOnEmptyWaveAndNextWave() { - waveTask.start(); - int waveNumber = waveTask.getCurrentWaveIndex(); - ServiceLocator.getWaveService().setEnemyCount(0); - waveTask.update(); - assertTrue(waveNumber + 1 == waveTask.getCurrentWaveIndex()); - } - -} diff --git a/source/core/src/test/com/csse3200/game/input/EngineerInputComponentTest.java b/source/core/src/test/com/csse3200/game/input/EngineerInputComponentTest.java new file mode 100644 index 000000000..6e6cb948a --- /dev/null +++ b/source/core/src/test/com/csse3200/game/input/EngineerInputComponentTest.java @@ -0,0 +1,37 @@ +package com.csse3200.game.input; + +import com.badlogic.gdx.Game; +import com.badlogic.gdx.graphics.Camera; +import com.csse3200.game.entities.EntityService; +import com.csse3200.game.services.ServiceLocator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class EngineerInputComponentTest { + private EngineerInputComponent engineerInputComponent; + + + @BeforeEach + public void setup() { + EntityService mockEntityService = mock(EntityService.class); + ServiceLocator.registerEntityService(mockEntityService); + + Camera mockCamera = mock(Camera.class); + Game game = mock(Game.class); + this.engineerInputComponent = new EngineerInputComponent(game, mockCamera); + } + + @Test + public void testTouchDownOnTileWithNoSelectedEngineer() { + when(ServiceLocator.getEntityService().getEntityAtPositionLayer(anyFloat(), anyFloat(), anyShort())).thenReturn(null); + // nothing happened -> false + boolean result = engineerInputComponent.touchDown(0, 0, 0, 0); + assertFalse(result); + // no engineer should be selected + assertNull(engineerInputComponent.selectedEngineer); + } +}