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/components/maingame/UIElementsDisplay.java b/source/core/src/main/com/csse3200/game/components/maingame/UIElementsDisplay.java index b84b24fe3..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 @@ -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; @@ -62,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, @@ -98,6 +84,10 @@ private void addActors() { } } + // Update the centrally located towerTypes list - + 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/entities/EntityService.java b/source/core/src/main/com/csse3200/game/entities/EntityService.java index d7dc5631e..a5e6c4d6f 100644 --- a/source/core/src/main/com/csse3200/game/entities/EntityService.java +++ b/source/core/src/main/com/csse3200/game/entities/EntityService.java @@ -226,7 +226,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 451c99129..1dcf527cf 100644 --- a/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java +++ b/source/core/src/main/com/csse3200/game/input/BuildInputComponent.java @@ -1,14 +1,18 @@ 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.CurrencyService; import com.csse3200.game.services.ServiceLocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +32,9 @@ public class BuildInputComponent extends InputComponent { }; private Sound buildSound; private Sound errorSound; + private Array towers = new Array<>(); + private Array defaultTowers = new Array<>(); + private boolean multipleTowerBuild = false; /** * Constructor for the BuildInputComponent @@ -37,6 +44,22 @@ public BuildInputComponent(Camera camera) { this.entityService = ServiceLocator.getEntityService(); this.camera = camera; loadSounds(); + towers.addAll(ServiceLocator.getTowerTypes()); + + logger.debug("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; + } } /** @@ -72,8 +95,57 @@ 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 buildTower((int)cursorPosition.x, (int)cursorPosition.y); + } else { + // TODO: Create a tile indication of invalid placement here?? + return false; + } + } + + /** + * 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) + */ + @Override + public boolean keyUp(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; + 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; @@ -86,14 +158,21 @@ public boolean touchDown(int screenX, int screenY, int pointer, int button) { * @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(); + currencyService = ServiceLocator.getCurrencyService(); + if (currencyService == null) { + // if the currency service fails or is not running + 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(); @@ -105,7 +184,14 @@ public void buildTower(int x, int y) { }; // build the selected tower newTower.setPosition(x, y); - ServiceLocator.getEntityService().register(newTower); + EntityService entityService; + + entityService = ServiceLocator.getEntityService(); + if (entityService == null){ + return false; + } + entityService.register(newTower); + // Decrement currency and show a popup that reflects the cost of the build ServiceLocator.getCurrencyService().getScrap().modify(-cost); ServiceLocator.getCurrencyService().getDisplay().updateScrapsStats(); @@ -113,12 +199,23 @@ public void buildTower(int x, int y) { long soundId = buildSound.play(); buildSound.setVolume(soundId, 0.4f); + + // deselect the tower after building + if (!multipleTowerBuild) { + ServiceLocator.getCurrencyService().setTowerType(null); + } + return true; } else { // play a sound to indicate an invalid action long soundId = errorSound.play(); - errorSound.setVolume(soundId, 0.5f); + errorSound.setVolume(soundId, 1f); + 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/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/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/main/com/csse3200/game/services/ServiceLocator.java b/source/core/src/main/com/csse3200/game/services/ServiceLocator.java index 8f3c72154..7c16fc03f 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,13 @@ public static void registerMapService(MapService source) { mapService = source; } - public static void setTowerTypes(Set selectedTowers) { + public static void setTowerTypes(Array selectedTowers) { + towerTypes.clear(); towerTypes.addAll(selectedTowers); } - public static Set getTowerTypes() { + public static Array getTowerTypes() { return towerTypes; } @@ -139,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 3cd427659..1b967498c 100644 --- a/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java +++ b/source/core/src/test/com/csse3200/game/input/BuildInputComponentTest.java @@ -1,159 +1,141 @@ -//package com.csse3200.game.input; -// -//import com.badlogic.gdx.graphics.g2d.TextureAtlas; -//import com.badlogic.gdx.maps.tiled.TiledMap; -//import com.badlogic.gdx.math.Vector2; -//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() { -// 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 = new MapService(camera); -// 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.csse3200.game.areas.terrain.TerrainComponent; +import com.csse3200.game.components.CameraComponent; +import com.csse3200.game.entities.EntityService; +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; +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); + assertFalse(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() { + 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 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 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)); + } +}