From 8a750520ea24bfdb9fcc50b10ac9cfa9324cf972 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Tue, 10 Oct 2023 09:15:36 +1000 Subject: [PATCH 1/7] modified tower build to deselect after a tower placement --- .../game/components/maingame/UIElementsDisplay.java | 11 ----------- .../com/csse3200/game/input/BuildInputComponent.java | 3 +++ .../com/csse3200/game/services/CurrencyService.java | 5 +++++ .../csse3200/game/input/BuildInputComponentTest.java | 9 ++++++++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java index b84b24fe3..514d2ae20 100644 --- a/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java @@ -2,28 +2,19 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.audio.Sound; -import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.math.GridPoint2; -import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.scenes.scene2d.Actor; -import com.badlogic.gdx.scenes.scene2d.ui.Button; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.utils.Array; -import com.csse3200.game.entities.Entity; -import com.csse3200.game.entities.factories.TowerFactory; import com.csse3200.game.screens.TowerType; import com.csse3200.game.services.ServiceLocator; -import com.csse3200.game.ui.ButtonFactory; import com.csse3200.game.ui.UIComponent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.HashSet; -import java.util.Set; /** * Displays a button to represent the remaining mobs left in the current wave and a button to skip to the next wave. @@ -40,8 +31,6 @@ public class UIElementsDisplay extends UIComponent { }; private Sound click; private Sound hover; -// private TextButton remainingMobsButton = new ButtonFactory().createButton("Mobs left:"); -// private final TextButton timerButton = new ButtonFactory().createButton("Next wave:"); private TextButton remainingMobsButton; private TextButton timerButton; private final int timer = 110; diff --git a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java index 451c99129..7116d8e31 100644 --- a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java @@ -113,6 +113,9 @@ public void buildTower(int x, int y) { long soundId = buildSound.play(); buildSound.setVolume(soundId, 0.4f); + + // deselect the tower after building + ServiceLocator.getCurrencyService().setTowerType(null); } else { // play a sound to indicate an invalid action long soundId = errorSound.play(); diff --git a/source/core/src/main/com/csse3200/game/services/CurrencyService.java b/source/core/src/main/com/csse3200/game/services/CurrencyService.java index d76f7b147..4221d7a9c 100644 --- a/source/core/src/main/com/csse3200/game/services/CurrencyService.java +++ b/source/core/src/main/com/csse3200/game/services/CurrencyService.java @@ -49,6 +49,11 @@ public CurrencyDisplay getDisplay() { return display; } + /** + * Sets the tower type to build - triggered by pressing a tower build button in-game + * newTower can be a towertype or a null value to indicate clearing the value? + * @param newTower The towertype to be set for building, null if deselecting + */ public void setTowerType(TowerType newTower) { if (tower == newTower) { tower = null; diff --git a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java index 3cd427659..4d6837a55 100644 --- a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java +++ b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java @@ -1,8 +1,11 @@ //package com.csse3200.game.input; // +//import com.badlogic.gdx.Gdx; +//import com.badlogic.gdx.Graphics; //import com.badlogic.gdx.graphics.g2d.TextureAtlas; //import com.badlogic.gdx.maps.tiled.TiledMap; //import com.badlogic.gdx.math.Vector2; +//import com.csse3200.game.GdxGame; //import com.csse3200.game.components.CameraComponent; //import com.csse3200.game.currency.Currency; //import com.csse3200.game.entities.Entity; @@ -58,6 +61,9 @@ // // @BeforeEach // void setup() { +// Gdx.graphics = mock(Graphics.class); +// when(Gdx.graphics.getDeltaTime()).thenReturn(10f); +// // GameTime gameTime = mock(GameTime.class); // CameraComponent camera = mock(CameraComponent.class); // when(gameTime.getDeltaTime()).thenReturn(0.02f); @@ -69,7 +75,8 @@ // // CurrencyService currencyService = new CurrencyService(); // ResourceService resourceService = new ResourceService(); -// MapService mapService = new MapService(camera); +// MapService mapService = mock(MapService.class); +// when(mapService.getComponent().tileToWorldPosition(0,0)).thenReturn(new Vector2(0,0)); // EntityService entityService = new EntityService(); // // ServiceLocator.registerResourceService(resourceService); From c9ad35f482fbed931e391b97169baa8e54b690e1 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:43:46 +1000 Subject: [PATCH 2/7] added shortcut key functionality for build menu --- .../maingame/UIElementsDisplay.java | 5 +++ .../game/input/BuildInputComponent.java | 35 +++++++++++++++++++ .../game/screens/TurretSelectionScreen.java | 8 ++++- .../game/services/ServiceLocator.java | 26 ++++++++++---- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java index 514d2ae20..0cd5b3e81 100644 --- a/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java @@ -87,6 +87,11 @@ private void addActors() { } } + // Update the centrally located towerTypes list - + logger.info("In UIElementsDisplay, the towers being sent to ServiceLocator are " + towers); + ServiceLocator.setTowerTypes(towers); + + // Create the buttons - TODO This needs overhauling to pretty buttons TextButton tower1 = new TextButton(towers.get(0).getTowerName(), skin); TextButton tower2 = new TextButton(towers.get(1).getTowerName(), skin); TextButton tower3 = new TextButton(towers.get(2).getTowerName(), skin); diff --git a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java index 7116d8e31..8f21e528b 100644 --- a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java @@ -1,15 +1,19 @@ package com.csse3200.game.input; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.utils.Array; import com.csse3200.game.areas.ForestGameArea; import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.EntityService; import com.csse3200.game.entities.factories.TowerFactory; import com.csse3200.game.screens.TowerType; import com.csse3200.game.services.ServiceLocator; +import com.csse3200.game.utils.math.Vector2Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +32,7 @@ public class BuildInputComponent extends InputComponent { }; private Sound buildSound; private Sound errorSound; + private Array towers = new Array<>(); /** * Constructor for the BuildInputComponent @@ -37,6 +42,7 @@ public BuildInputComponent(Camera camera) { this.entityService = ServiceLocator.getEntityService(); this.camera = camera; loadSounds(); + towers.addAll(ServiceLocator.getTowerTypes()); } /** @@ -79,6 +85,35 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { return false; } + /** + * Triggers player events on specific keycodes. + * + * @return whether the input was processed + * @see InputProcessor#keyDown(int) + */ + @Override + public boolean keyDown(int keycode) { + switch (keycode) { + case Input.Keys.NUM_1: + ServiceLocator.getCurrencyService().setTowerType(towers.get(0)); + return true; + case Input.Keys.NUM_2: + ServiceLocator.getCurrencyService().setTowerType(towers.get(1)); + return true; + case Input.Keys.NUM_3: + ServiceLocator.getCurrencyService().setTowerType(towers.get(2)); + return true; + case Input.Keys.NUM_4: + ServiceLocator.getCurrencyService().setTowerType(towers.get(3)); + return true; + case Input.Keys.NUM_5: + ServiceLocator.getCurrencyService().setTowerType(towers.get(4)); + return true; + default: + return false; + } + } + /** * Instantiates and spawns the selected tower at the given x y coordinates on the tile map. Assumes that the given * x and y coordinate is valid and that the TowerType exists in the CurrencyService. diff --git a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java index e27598388..fc768374d 100644 --- a/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java +++ b/source/core/src/main/com/csse3200/game/screens/TurretSelectionScreen.java @@ -20,6 +20,7 @@ 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.badlogic.gdx.utils.viewport.ScreenViewport; import com.csse3200.game.GdxGame; import com.csse3200.game.services.ResourceService; @@ -115,7 +116,12 @@ public void clicked(InputEvent event, float x, float y) { @Override public void clicked(InputEvent event, float x, float y) { // Store the selected towers in the ServiceLocator for transferring across screens - ServiceLocator.setTowerTypes(selectedTurrets);; + // (as an Array) + Array towers = new Array<>(); + for (TowerType t : selectedTurrets) { + towers.add(t); + } + ServiceLocator.setTowerTypes(towers);; game.setScreen(GdxGame.ScreenType.MAIN_GAME); } }); 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 8f3c72154..1df468c8f 100644 --- a/source/core/src/main/com/csse3200/game/services/ServiceLocator.java +++ b/source/core/src/main/com/csse3200/game/services/ServiceLocator.java @@ -33,7 +33,7 @@ public class ServiceLocator { private static WaveService waveService; private static MapService mapService; - private static Set towerTypes = new HashSet<>(); + private static Array towerTypes = new Array<>(); public static CurrencyService getCurrencyService() { return currencyService; @@ -120,12 +120,24 @@ public static void registerMapService(MapService source) { mapService = source; } - public static void setTowerTypes(Set selectedTowers) { - towerTypes.clear(); - towerTypes.addAll(selectedTowers); - } - - public static Set getTowerTypes() { + public static void setTowerTypes(Array selectedTowers) { + if (towerTypes.isEmpty()) { + // set default towers + TowerType[] defaultTowers = { + TowerType.TNT, + TowerType.DROID, + TowerType.INCOME, + TowerType.WALL, + TowerType.WEAPON + }; + towerTypes.addAll(defaultTowers); + } else{ + towerTypes.clear(); + towerTypes.addAll(selectedTowers); + } + } + + public static Array getTowerTypes() { return towerTypes; } From 03c09059ee00b0ea7f4b41fa6afa264eb0e05970 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Tue, 10 Oct 2023 22:40:41 +1000 Subject: [PATCH 3/7] updated buildInputComponent test --- .../maingame/UIElementsDisplay.java | 1 - .../csse3200/game/entities/EntityService.java | 2 +- .../game/input/BuildInputComponent.java | 12 +- .../game/input/BuildInputComponentTest.java | 284 ++++++++---------- 4 files changed, 128 insertions(+), 171 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java index 0cd5b3e81..42bd789ef 100644 --- a/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java @@ -88,7 +88,6 @@ private void addActors() { } // Update the centrally located towerTypes list - - logger.info("In UIElementsDisplay, the towers being sent to ServiceLocator are " + towers); ServiceLocator.setTowerTypes(towers); // Create the buttons - TODO This needs overhauling to pretty buttons diff --git a/source/core/src/main/com/csse3200/game/entities/EntityService.java b/source/core/src/main/com/csse3200/game/entities/EntityService.java index f45576fb0..95af09587 100644 --- a/source/core/src/main/com/csse3200/game/entities/EntityService.java +++ b/source/core/src/main/com/csse3200/game/entities/EntityService.java @@ -221,7 +221,7 @@ public boolean entitiesInTile(int x_coord, int y_coord) { try { mp = (TiledMapTileLayer)ServiceLocator.getMapService().getComponent().getMap().getLayers().get(0); } catch (NullPointerException e) { - // MapService is not running + // MapService is not running - consider this occupied (invalid tile) return true; } if (mp.getCell(x_coord, y_coord) != null) { diff --git a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java index 8f21e528b..5c87099d3 100644 --- a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java @@ -81,12 +81,15 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { buildTower((int)cursorPosition.x, (int)cursorPosition.y); logger.debug("spawning a tower at {}, {}", cursorPosition.x, cursorPosition.y); return true; + } else { + // TODO: Create a tile indication of invalid placement here?? + return false; } - return false; } /** - * Triggers player events on specific keycodes. + * Configures shortcut keys for building towers. Pressing the shortcut key + * sets the 'tower to build' variable in CurrencyService * * @return whether the input was processed * @see InputProcessor#keyDown(int) @@ -141,6 +144,7 @@ public void buildTower(int x, int y) { // build the selected tower newTower.setPosition(x, y); ServiceLocator.getEntityService().register(newTower); + // Decrement currency and show a popup that reflects the cost of the build ServiceLocator.getCurrencyService().getScrap().modify(-cost); ServiceLocator.getCurrencyService().getDisplay().updateScrapsStats(); @@ -154,7 +158,9 @@ public void buildTower(int x, int y) { } else { // play a sound to indicate an invalid action long soundId = errorSound.play(); - errorSound.setVolume(soundId, 0.5f); + errorSound.setVolume(soundId, 1f); + // TODO: add a visual indication of the build fail, through + // currency display flash } } } diff --git a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java index 4d6837a55..fc48e2e3c 100644 --- a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java +++ b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java @@ -1,166 +1,118 @@ -//package com.csse3200.game.input; -// -//import com.badlogic.gdx.Gdx; -//import com.badlogic.gdx.Graphics; -//import com.badlogic.gdx.graphics.g2d.TextureAtlas; -//import com.badlogic.gdx.maps.tiled.TiledMap; -//import com.badlogic.gdx.math.Vector2; -//import com.csse3200.game.GdxGame; -//import com.csse3200.game.components.CameraComponent; -//import com.csse3200.game.currency.Currency; -//import com.csse3200.game.entities.Entity; -//import com.csse3200.game.entities.EntityService; -//import com.csse3200.game.entities.factories.TowerFactory; -//import com.csse3200.game.extensions.GameExtension; -//import com.csse3200.game.physics.PhysicsService; -//import com.csse3200.game.rendering.DebugRenderer; -//import com.csse3200.game.rendering.RenderService; -//import com.csse3200.game.services.*; -//import org.junit.jupiter.api.AfterEach; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.extension.ExtendWith; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -//import static org.junit.jupiter.api.Assertions.assertFalse; -//import static org.mockito.Mockito.*; -// -//@ExtendWith(GameExtension.class) -//class BuildInputComponentTest { -// -// private BuildInputComponent buildInputComponent; -// private Entity baseTower; -// private Entity weaponTower; -// private Entity wallTower; -// private Entity stunTower; -// private Entity fireTower; -// private Entity tntTower; -// private Entity droidTower; -// private String[] texture = { -// "images/towers/turret_deployed.png", -// "images/towers/turret01.png", -// "images/towers/wall_tower.png", -// "images/towers/fire_tower_atlas.png", -// "images/towers/stun_tower.png", -// "images/towers/DroidTower.png", -// "images/towers/TNTTower.png" -// }; -// private String[] atlas = { -// "images/towers/turret01.atlas", -// "images/towers/stun_tower.atlas", -// "images/towers/fire_tower_atlas.atlas", -// "images/towers/DroidTower.atlas", -// "images/towers/TNTTower.atlas", -// "images/towers/barrier.atlas" -// }; -// private static final String[] sounds = { -// "sounds/towers/gun_shot_trimmed.mp3", -// "sounds/towers/deploy.mp3", -// "sounds/towers/stow.mp3" -// }; -// -// @BeforeEach -// void setup() { -// Gdx.graphics = mock(Graphics.class); -// when(Gdx.graphics.getDeltaTime()).thenReturn(10f); -// -// GameTime gameTime = mock(GameTime.class); -// CameraComponent camera = mock(CameraComponent.class); -// when(gameTime.getDeltaTime()).thenReturn(0.02f); -// ServiceLocator.registerTimeSource(gameTime); -// ServiceLocator.registerPhysicsService(new PhysicsService()); -// RenderService render = new RenderService(); -// render.setDebug(mock(DebugRenderer.class)); -// ServiceLocator.registerRenderService(render); -// -// CurrencyService currencyService = new CurrencyService(); -// ResourceService resourceService = new ResourceService(); -// MapService mapService = mock(MapService.class); -// when(mapService.getComponent().tileToWorldPosition(0,0)).thenReturn(new Vector2(0,0)); -// EntityService entityService = new EntityService(); -// -// ServiceLocator.registerResourceService(resourceService); -// ServiceLocator.registerCurrencyService(currencyService); -// ServiceLocator.registerMapService(mapService); -// ServiceLocator.registerEntityService(entityService); -// -// resourceService.loadTextures(texture); -// resourceService.loadTextureAtlases(atlas); -// resourceService.loadSounds(sounds); -// resourceService.loadAll(); -// -// ServiceLocator.getResourceService() -// .getAsset("images/towers/turret01.atlas", TextureAtlas.class); -// baseTower = TowerFactory.createBaseTower(); -// weaponTower = TowerFactory.createWeaponTower(); -// wallTower = TowerFactory.createWallTower(); -// fireTower = TowerFactory.createFireTower(); -// stunTower = TowerFactory.createFireTower(); -// tntTower = TowerFactory.createTNTTower(); -// droidTower = TowerFactory.createDroidTower(); -// -// buildInputComponent = new BuildInputComponent(camera.getCamera()); -// } -// -// @Test -// void shouldUpdatePriority() { -// int newPriority = 100; -// InputComponent inputComponent = spy(InputComponent.class); -// -// inputComponent.setPriority(newPriority); -// verify(inputComponent).setPriority(newPriority); -// -// int priority = inputComponent.getPriority(); -// verify(inputComponent).getPriority(); -// -// assertEquals(newPriority, priority); -// } -// -// @Test -// void shouldRegisterOnCreate() { -// InputService inputService = spy(InputService.class); -// ServiceLocator.registerInputService(inputService); -// -// InputComponent inputComponent = spy(InputComponent.class); -// inputComponent.create(); -// verify(inputService).register(inputComponent); -// } -// -// @Test -// void shouldHandleTouchDown() { -// BuildInputComponent inputComponent = spy(BuildInputComponent.class); -// assertFalse(inputComponent.touchDown( 5, 6, 7, 8)); -// } -// -// @Test -// void shouldRejectOccupiedTile() { -// Vector2 tile = ServiceLocator.getMapService().getComponent().tileToWorldPosition(0, 0); -// tntTower.setPosition(0,0); -// assertFalse(buildInputComponent.touchDown(0,0, 7,8)); -// } -// -// @Test -// void shouldRejectInvalidTile() { -// -// } -// -// @Test -// void shouldHandleMissingMapService() { -// -// } -// -// @Test -// void shouldHandleMissingCurrencyService() { -// -// } -// -// @Test -// void shouldHandleInvalidTower() { -// -// } -// -// @Test -// void shouldHandleMissingEntityService() { -// -// } -//} +package com.csse3200.game.input; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Graphics; +import com.badlogic.gdx.graphics.Camera; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.areas.terrain.TerrainComponent; +import com.csse3200.game.components.CameraComponent; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.EntityService; +import com.csse3200.game.entities.factories.TowerFactory; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.rendering.DebugRenderer; +import com.csse3200.game.rendering.RenderService; +import com.csse3200.game.services.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.*; + +@ExtendWith(GameExtension.class) +class BuildInputComponentTest { + + private BuildInputComponent buildInputComponent; + EntityService entityService; + + @BeforeEach + void setup() { + Gdx.graphics = mock(Graphics.class); + when(Gdx.graphics.getDeltaTime()).thenReturn(10f); + + GameTime gameTime = mock(GameTime.class); + CameraComponent camera = mock(CameraComponent.class); + when(camera.getCamera()).thenReturn(mock(Camera.class)); + when(gameTime.getDeltaTime()).thenReturn(0.02f); + ServiceLocator.registerTimeSource(gameTime); + ServiceLocator.registerPhysicsService(new PhysicsService()); + RenderService render = new RenderService(); + render.setDebug(mock(DebugRenderer.class)); + ServiceLocator.registerRenderService(render); + + CurrencyService currencyService = new CurrencyService(); + ResourceService resourceService = new ResourceService(); + MapService mapService = mock(MapService.class); + when(mapService.getComponent()).thenReturn(mock(TerrainComponent.class)); + entityService = mock(EntityService.class); + + ServiceLocator.registerResourceService(resourceService); + ServiceLocator.registerCurrencyService(currencyService); + ServiceLocator.registerMapService(mapService); + ServiceLocator.registerEntityService(entityService); + + buildInputComponent = new BuildInputComponent(camera.getCamera()); + } + + @Test + void shouldUpdatePriority() { + int newPriority = 100; + InputComponent inputComponent = spy(InputComponent.class); + + inputComponent.setPriority(newPriority); + verify(inputComponent).setPriority(newPriority); + + int priority = inputComponent.getPriority(); + verify(inputComponent).getPriority(); + + assertEquals(newPriority, priority); + } + + @Test + void shouldRegisterOnCreate() { + InputService inputService = spy(InputService.class); + ServiceLocator.registerInputService(inputService); + + InputComponent inputComponent = spy(InputComponent.class); + inputComponent.create(); + verify(inputService).register(inputComponent); + } + + @Test + void shouldHandleTouchDown() { + when(entityService.entitiesInTile(5, 5)).thenReturn(false); + assert(buildInputComponent.touchDown( 5, 5, 7, 8)); + } + + @Test + void shouldRejectOccupiedOrInvalidTile() { + // entitiesInTile checks for out of bounds condition as well + when(entityService.entitiesInTile(5, 5)).thenReturn(true); + assertFalse(buildInputComponent.touchDown(5,5, 7,8), + "Attempting to build on an existing tower should return False"); + } + + @Test + void shouldHandleMissingMapService() { + + } + + @Test + void shouldHandleMissingCurrencyService() { + + } + + @Test + void shouldHandleInvalidTower() { + + } + + @Test + void shouldHandleMissingEntityService() { + + } +} From 6215af197dde5aeb4f62a274197d910959fd555c Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Wed, 11 Oct 2023 13:15:42 +1000 Subject: [PATCH 4/7] fixed key shortcut bug that set towers to default --- .../components/gamearea/CurrencyDisplay.java | 11 ++++++ .../game/input/BuildInputComponent.java | 38 +++++++++++++++---- .../game/services/ServiceLocator.java | 18 ++------- .../com/csse3200/game/ui/UIComponent.java | 4 ++ .../game/input/BuildInputComponentTest.java | 37 ++++++++++++++---- 5 files changed, 79 insertions(+), 29 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/gamearea/CurrencyDisplay.java b/source/core/src/main/com/csse3200/game/components/gamearea/CurrencyDisplay.java index a887ed896..a2c348b74 100644 --- a/source/core/src/main/com/csse3200/game/components/gamearea/CurrencyDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/gamearea/CurrencyDisplay.java @@ -2,6 +2,7 @@ import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.graphics.Camera; +import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; @@ -15,6 +16,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.Drawable; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.utils.Align; +import com.csse3200.game.services.GameTime; import com.csse3200.game.services.ServiceLocator; import com.csse3200.game.ui.UIComponent; import com.badlogic.gdx.scenes.scene2d.actions.Actions; @@ -89,6 +91,15 @@ public void updateScrapsStats() { scrapsTb.getLabel().setText(text); } + /** + * Displays a warning animation of the scraps display if the player tries to + * build something that costs more than the balance + */ + public void scrapBalanceFlash() { + // TODO: IMPLEMENT THIS + scrapsTb.setText("Insufficient!"); + } + /** * Updates the currency (Crystals) value on the UI component */ diff --git a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java index 5c87099d3..11fed6a5e 100644 --- a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java @@ -13,7 +13,6 @@ import com.csse3200.game.entities.factories.TowerFactory; import com.csse3200.game.screens.TowerType; import com.csse3200.game.services.ServiceLocator; -import com.csse3200.game.utils.math.Vector2Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +32,7 @@ public class BuildInputComponent extends InputComponent { private Sound buildSound; private Sound errorSound; private Array towers = new Array<>(); + private Array defaultTowers = new Array<>(); /** * Constructor for the BuildInputComponent @@ -43,6 +43,21 @@ public BuildInputComponent(Camera camera) { this.camera = camera; loadSounds(); towers.addAll(ServiceLocator.getTowerTypes()); + +// logger.info("selected towers in buildInputComponent are " + towers); + TowerType[] defaultTowerTypes = { + TowerType.TNT, + TowerType.DROID, + TowerType.INCOME, + TowerType.WALL, + TowerType.WEAPON + }; + defaultTowers.addAll(defaultTowerTypes); + + if (towers.isEmpty()) { + ServiceLocator.setTowerTypes(defaultTowers); + towers = defaultTowers; + } } /** @@ -74,12 +89,12 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { // determine if the tile is unoccupied boolean tileOccupied = entityService.entitiesInTile((int)cursorPosition.x, (int)cursorPosition.y); - logger.debug("Tile is occupied: " + tileOccupied ); +// logger.debug("Tile is occupied: " + tileOccupied ); // check that no entities are occupying the tile if (!tileOccupied) { buildTower((int)cursorPosition.x, (int)cursorPosition.y); - logger.debug("spawning a tower at {}, {}", cursorPosition.x, cursorPosition.y); +// logger.debug("spawning a tower at {}, {}", cursorPosition.x, cursorPosition.y); return true; } else { // TODO: Create a tile indication of invalid placement here?? @@ -95,7 +110,7 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { * @see InputProcessor#keyDown(int) */ @Override - public boolean keyDown(int keycode) { + public boolean keyUp(int keycode) { switch (keycode) { case Input.Keys.NUM_1: ServiceLocator.getCurrencyService().setTowerType(towers.get(0)); @@ -126,6 +141,7 @@ public boolean keyDown(int keycode) { */ public void buildTower(int x, int y) { // fetch the currently set TowerType in the currency service, and its associated build cost. + TowerType tower = ServiceLocator.getCurrencyService().getTower(); if (tower != null) { // fetch the price of the selected tower and attempt to instantiate @@ -159,6 +175,7 @@ public void buildTower(int x, int y) { // play a sound to indicate an invalid action long soundId = errorSound.play(); errorSound.setVolume(soundId, 1f); + ServiceLocator.getCurrencyService().getDisplay().scrapBalanceFlash(); // TODO: add a visual indication of the build fail, through // currency display flash } @@ -169,9 +186,14 @@ public void buildTower(int x, int y) { * Load the sound assets related to in-game tower building activity */ private void loadSounds() { - ServiceLocator.getResourceService().loadSounds(sounds); - ServiceLocator.getResourceService().loadAll(); - buildSound = ServiceLocator.getResourceService().getAsset("sounds/economy/buildSound.ogg", Sound.class); - errorSound = ServiceLocator.getResourceService().getAsset("sounds/ui/Switch/NA_SFUI_Vol1_switch_01.ogg", Sound.class); + try { + ServiceLocator.getResourceService().loadSounds(sounds); + ServiceLocator.getResourceService().loadAll(); + buildSound = ServiceLocator.getResourceService().getAsset("sounds/economy/buildSound.ogg", Sound.class); + errorSound = ServiceLocator.getResourceService().getAsset("sounds/ui/Switch/NA_SFUI_Vol1_switch_01.ogg", Sound.class); + + } catch (NullPointerException e) { + logger.error("BuildInputComponent line 173: Couldn't load sounds for build menu"); + } } } 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 1df468c8f..7c16fc03f 100644 --- a/source/core/src/main/com/csse3200/game/services/ServiceLocator.java +++ b/source/core/src/main/com/csse3200/game/services/ServiceLocator.java @@ -121,20 +121,9 @@ public static void registerMapService(MapService source) { } public static void setTowerTypes(Array selectedTowers) { - if (towerTypes.isEmpty()) { - // set default towers - TowerType[] defaultTowers = { - TowerType.TNT, - TowerType.DROID, - TowerType.INCOME, - TowerType.WALL, - TowerType.WEAPON - }; - towerTypes.addAll(defaultTowers); - } else{ - towerTypes.clear(); - towerTypes.addAll(selectedTowers); - } + + towerTypes.clear(); + towerTypes.addAll(selectedTowers); } public static Array getTowerTypes() { @@ -151,6 +140,7 @@ public static void clear() { gameEndService = null; waveService = null; mapService = null; + towerTypes.clear(); } private ServiceLocator() { diff --git a/source/core/src/main/com/csse3200/game/ui/UIComponent.java b/source/core/src/main/com/csse3200/game/ui/UIComponent.java index 60cfe7009..025282e70 100644 --- a/source/core/src/main/com/csse3200/game/ui/UIComponent.java +++ b/source/core/src/main/com/csse3200/game/ui/UIComponent.java @@ -32,4 +32,8 @@ public int getLayer() { public float getZIndex() { return 1f; } + + public static Skin getSkin() { + return skin; + } } diff --git a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java index fc48e2e3c..61f2d1935 100644 --- a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java +++ b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java @@ -3,17 +3,14 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Graphics; import com.badlogic.gdx.graphics.Camera; -import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.math.Vector2; import com.csse3200.game.areas.terrain.TerrainComponent; import com.csse3200.game.components.CameraComponent; -import com.csse3200.game.entities.Entity; import com.csse3200.game.entities.EntityService; -import com.csse3200.game.entities.factories.TowerFactory; import com.csse3200.game.extensions.GameExtension; import com.csse3200.game.physics.PhysicsService; import com.csse3200.game.rendering.DebugRenderer; import com.csse3200.game.rendering.RenderService; +import com.csse3200.game.screens.TowerType; import com.csse3200.game.services.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -98,21 +95,47 @@ void shouldRejectOccupiedOrInvalidTile() { @Test void shouldHandleMissingMapService() { - + when(ServiceLocator.getMapService()).thenReturn(null); + assertFalse(buildInputComponent.touchDown(5,5,7,8)); } @Test void shouldHandleMissingCurrencyService() { + when(ServiceLocator.getCurrencyService()).thenReturn(null); + assertFalse(buildInputComponent.touchDown(5,5,7,8)); + } + @Test + void shouldHandleNullTowerName() { + TowerType towerType = mock(TowerType.class); + when(towerType.getTowerName()).thenReturn(null); + ServiceLocator.getCurrencyService().setTowerType(towerType); } @Test - void shouldHandleInvalidTower() { + void shouldHandleNullTowerDesc() { + TowerType towerType = mock(TowerType.class); + when(towerType.getDescription()).thenReturn(null); + ServiceLocator.getCurrencyService().setTowerType(towerType); + } + @Test + void shouldHandleNullTowerCost() { + TowerType towerType = mock(TowerType.class); + when(towerType.getPrice()).thenReturn(null); + ServiceLocator.getCurrencyService().setTowerType(towerType); } @Test - void shouldHandleMissingEntityService() { + void shouldHandleInvalidTowerName() { + TowerType towerType = mock(TowerType.class); + when(towerType.getTowerName()).thenReturn(null); + ServiceLocator.getCurrencyService().setTowerType(towerType); + } + @Test + void shouldHandleMissingEntityService() { + when(ServiceLocator.getEntityService()).thenReturn(null); + assertFalse(buildInputComponent.touchDown(5,5,7,8)); } } From ec25e7e4bf66ea60c28538beaaf0c0ff3175633f Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Wed, 11 Oct 2023 13:35:39 +1000 Subject: [PATCH 5/7] updated buildInputComponent JUnit tests --- .../game/input/BuildInputComponent.java | 36 ++++++++++++++----- .../game/input/BuildInputComponentTest.java | 2 +- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java index 11fed6a5e..8af5618c8 100644 --- a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java @@ -12,6 +12,7 @@ import com.csse3200.game.entities.EntityService; import com.csse3200.game.entities.factories.TowerFactory; import com.csse3200.game.screens.TowerType; +import com.csse3200.game.services.CurrencyService; import com.csse3200.game.services.ServiceLocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,9 +94,8 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { // check that no entities are occupying the tile if (!tileOccupied) { - buildTower((int)cursorPosition.x, (int)cursorPosition.y); // logger.debug("spawning a tower at {}, {}", cursorPosition.x, cursorPosition.y); - return true; + return buildTower((int)cursorPosition.x, (int)cursorPosition.y); } else { // TODO: Create a tile indication of invalid placement here?? return false; @@ -139,15 +139,24 @@ public boolean keyUp(int keycode) { * @param x x-coordinate int value * @param y y-coordinate int value */ - public void buildTower(int x, int y) { + public boolean buildTower(int x, int y) { + TowerType tower; + CurrencyService currencyService; // fetch the currently set TowerType in the currency service, and its associated build cost. - - TowerType tower = ServiceLocator.getCurrencyService().getTower(); + try { + currencyService = ServiceLocator.getCurrencyService(); + } catch (NullPointerException e) { + // if the currency service fails or is not running + logger.error("BuildInputComponent line 148: Failed to fetch currency service"); + // Set to default weaponTower + return false; + } + tower = currencyService.getTower(); if (tower != null) { // fetch the price of the selected tower and attempt to instantiate - int cost = Integer.parseInt(ServiceLocator.getCurrencyService().getTower().getPrice()); + int cost = Integer.parseInt(currencyService.getTower().getPrice()); - if (cost <= ServiceLocator.getCurrencyService().getScrap().getAmount()) { + if (cost <= currencyService.getScrap().getAmount()) { Entity newTower = switch (tower) { case WEAPON -> TowerFactory.createWeaponTower(); case INCOME -> TowerFactory.createIncomeTower(); @@ -159,7 +168,15 @@ public void buildTower(int x, int y) { }; // build the selected tower newTower.setPosition(x, y); - ServiceLocator.getEntityService().register(newTower); + EntityService entityService; + try { + entityService = ServiceLocator.getEntityService(); + } catch (NullPointerException e) { + // failed to fetch entityService + logger.error("BuildInputComponent line 173: Failed to fetch EntityService"); + return false; + } + entityService.register(newTower); // Decrement currency and show a popup that reflects the cost of the build ServiceLocator.getCurrencyService().getScrap().modify(-cost); @@ -171,6 +188,7 @@ public void buildTower(int x, int y) { // deselect the tower after building ServiceLocator.getCurrencyService().setTowerType(null); + return true; } else { // play a sound to indicate an invalid action long soundId = errorSound.play(); @@ -178,8 +196,10 @@ public void buildTower(int x, int y) { ServiceLocator.getCurrencyService().getDisplay().scrapBalanceFlash(); // TODO: add a visual indication of the build fail, through // currency display flash + } } + return false; } /** diff --git a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java index 61f2d1935..b67900aa8 100644 --- a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java +++ b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java @@ -82,7 +82,7 @@ void shouldRegisterOnCreate() { @Test void shouldHandleTouchDown() { when(entityService.entitiesInTile(5, 5)).thenReturn(false); - assert(buildInputComponent.touchDown( 5, 5, 7, 8)); + assertFalse(buildInputComponent.touchDown( 5, 5, 7, 8)); } @Test From 56e1267584ac295527c49c08e0a83193eaa6aec5 Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Thu, 12 Oct 2023 22:54:05 +1000 Subject: [PATCH 6/7] removed try catch statements --- .../game/input/BuildInputComponent.java | 36 +++++++------------ .../game/input/BuildInputComponentTest.java | 10 +++--- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java index 8af5618c8..15b58516f 100644 --- a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java @@ -45,7 +45,7 @@ public BuildInputComponent(Camera camera) { loadSounds(); towers.addAll(ServiceLocator.getTowerTypes()); -// logger.info("selected towers in buildInputComponent are " + towers); + logger.debug("selected towers in buildInputComponent are " + towers); TowerType[] defaultTowerTypes = { TowerType.TNT, TowerType.DROID, @@ -90,11 +90,11 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { // determine if the tile is unoccupied boolean tileOccupied = entityService.entitiesInTile((int)cursorPosition.x, (int)cursorPosition.y); -// logger.debug("Tile is occupied: " + tileOccupied ); + logger.debug("Tile is occupied: " + tileOccupied ); // check that no entities are occupying the tile if (!tileOccupied) { -// logger.debug("spawning a tower at {}, {}", cursorPosition.x, cursorPosition.y); + logger.debug("spawning a tower at {}, {}", cursorPosition.x, cursorPosition.y); return buildTower((int)cursorPosition.x, (int)cursorPosition.y); } else { // TODO: Create a tile indication of invalid placement here?? @@ -143,12 +143,9 @@ public boolean buildTower(int x, int y) { TowerType tower; CurrencyService currencyService; // fetch the currently set TowerType in the currency service, and its associated build cost. - try { - currencyService = ServiceLocator.getCurrencyService(); - } catch (NullPointerException e) { + currencyService = ServiceLocator.getCurrencyService(); + if (currencyService == null) { // if the currency service fails or is not running - logger.error("BuildInputComponent line 148: Failed to fetch currency service"); - // Set to default weaponTower return false; } tower = currencyService.getTower(); @@ -169,14 +166,12 @@ public boolean buildTower(int x, int y) { // build the selected tower newTower.setPosition(x, y); EntityService entityService; - try { - entityService = ServiceLocator.getEntityService(); - } catch (NullPointerException e) { - // failed to fetch entityService - logger.error("BuildInputComponent line 173: Failed to fetch EntityService"); + + entityService = ServiceLocator.getEntityService(); + if (entityService == null){ return false; } - entityService.register(newTower); + entityService.register(newTower); // Decrement currency and show a popup that reflects the cost of the build ServiceLocator.getCurrencyService().getScrap().modify(-cost); @@ -206,14 +201,9 @@ public boolean buildTower(int x, int y) { * Load the sound assets related to in-game tower building activity */ private void loadSounds() { - try { - ServiceLocator.getResourceService().loadSounds(sounds); - ServiceLocator.getResourceService().loadAll(); - buildSound = ServiceLocator.getResourceService().getAsset("sounds/economy/buildSound.ogg", Sound.class); - errorSound = ServiceLocator.getResourceService().getAsset("sounds/ui/Switch/NA_SFUI_Vol1_switch_01.ogg", Sound.class); - - } catch (NullPointerException e) { - logger.error("BuildInputComponent line 173: Couldn't load sounds for build menu"); - } + ServiceLocator.getResourceService().loadSounds(sounds); + ServiceLocator.getResourceService().loadAll(); + buildSound = ServiceLocator.getResourceService().getAsset("sounds/economy/buildSound.ogg", Sound.class); + errorSound = ServiceLocator.getResourceService().getAsset("sounds/ui/Switch/NA_SFUI_Vol1_switch_01.ogg", Sound.class); } } diff --git a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java index b67900aa8..1b967498c 100644 --- a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java +++ b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java @@ -86,7 +86,7 @@ void shouldHandleTouchDown() { } @Test - void shouldRejectOccupiedOrInvalidTile() { + void shouldRejectOccupiedOrInvalidTile() { // entitiesInTile checks for out of bounds condition as well when(entityService.entitiesInTile(5, 5)).thenReturn(true); assertFalse(buildInputComponent.touchDown(5,5, 7,8), @@ -94,19 +94,19 @@ void shouldRejectOccupiedOrInvalidTile() { } @Test - void shouldHandleMissingMapService() { + void shouldHandleMissingMapService() { when(ServiceLocator.getMapService()).thenReturn(null); assertFalse(buildInputComponent.touchDown(5,5,7,8)); } @Test - void shouldHandleMissingCurrencyService() { + void shouldHandleMissingCurrencyService() { when(ServiceLocator.getCurrencyService()).thenReturn(null); assertFalse(buildInputComponent.touchDown(5,5,7,8)); } @Test - void shouldHandleNullTowerName() { + void shouldHandleNullTowerName() { TowerType towerType = mock(TowerType.class); when(towerType.getTowerName()).thenReturn(null); ServiceLocator.getCurrencyService().setTowerType(towerType); @@ -134,7 +134,7 @@ void shouldHandleInvalidTowerName() { } @Test - void shouldHandleMissingEntityService() { + void shouldHandleMissingEntityService() { when(ServiceLocator.getEntityService()).thenReturn(null); assertFalse(buildInputComponent.touchDown(5,5,7,8)); } From 18a7a6ff582d9e87b3ac55045a3f32a49806c3dd Mon Sep 17 00:00:00 2001 From: Ahmad Abu-Aysha <111224176+The-AhmadAA@users.noreply.github.com> Date: Thu, 12 Oct 2023 23:43:55 +1000 Subject: [PATCH 7/7] added multiple placement key functionality --- .../maingame/UIElementsDisplay.java | 5 +--- .../game/input/BuildInputComponent.java | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java index 42bd789ef..be5f377c3 100644 --- a/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java @@ -51,14 +51,11 @@ private void addActors() { remainingMobsButton = new TextButton("Mobs:" + ServiceLocator.getWaveService().getEnemyCount(), skin); buttonTable.top().right(); - towerTable.top(); + towerTable.top().padTop(80f); buttonTable.setFillParent(true); towerTable.setFillParent(true); - towerTable.setDebug(true); - towerTable.padTop(50f); - TowerType[] defaultTowers = { TowerType.TNT, TowerType.DROID, diff --git a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java index 15b58516f..1dcf527cf 100644 --- a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java @@ -34,6 +34,7 @@ public class BuildInputComponent extends InputComponent { private Sound errorSound; private Array towers = new Array<>(); private Array defaultTowers = new Array<>(); + private boolean multipleTowerBuild = false; /** * Constructor for the BuildInputComponent @@ -127,11 +128,29 @@ public boolean keyUp(int keycode) { case Input.Keys.NUM_5: ServiceLocator.getCurrencyService().setTowerType(towers.get(4)); return true; + case Input.Keys.CONTROL_LEFT: + // After multiple placement, deselect tower and prevent further builds + ServiceLocator.getCurrencyService().setTowerType(null); + multipleTowerBuild = false; + return true; default: return false; } } + /** + * + * @param keycode one of the constants in {@link Input.Keys} + * @return + */ + public boolean keyDown(int keycode) { + if (keycode == Input.Keys.CONTROL_LEFT) { + multipleTowerBuild = true; + return true; + } + return false; + } + /** * Instantiates and spawns the selected tower at the given x y coordinates on the tile map. Assumes that the given * x and y coordinate is valid and that the TowerType exists in the CurrencyService. @@ -182,7 +201,9 @@ public boolean buildTower(int x, int y) { buildSound.setVolume(soundId, 0.4f); // deselect the tower after building - ServiceLocator.getCurrencyService().setTowerType(null); + if (!multipleTowerBuild) { + ServiceLocator.getCurrencyService().setTowerType(null); + } return true; } else { // play a sound to indicate an invalid action