From 455550659ce2f8335e4934d1e7f8a230a5937361 Mon Sep 17 00:00:00 2001 From: s4809965 Date: Thu, 10 Oct 2024 22:42:14 +1000 Subject: [PATCH 01/14] #509 Added keyboard input UI images and displays them with the InventoryDisplayHoverComponent --- .../images/inventory_ui/combine_key.png | Bin 0 -> 528 bytes .../images/inventory_ui/interact_key.png | Bin 0 -> 542 bytes .../assets/images/inventory_ui/rotate_key.png | Bin 0 -> 676 bytes .../InventoryDisplayHoverComponent.java | 21 ++++++++++++++++++ .../entities/factories/StationFactory.java | 3 ++- 5 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 source/core/assets/images/inventory_ui/combine_key.png create mode 100644 source/core/assets/images/inventory_ui/interact_key.png create mode 100644 source/core/assets/images/inventory_ui/rotate_key.png diff --git a/source/core/assets/images/inventory_ui/combine_key.png b/source/core/assets/images/inventory_ui/combine_key.png new file mode 100644 index 0000000000000000000000000000000000000000..de79127f9142cd419bd0cdf4ad89a02d9f27d567 GIT binary patch literal 528 zcmV+r0`L8aP)Px#1ZP1_K>z@;j|==^1poj53{Xr|MLkfnPw3n70WS3oFY5SPjq1y73HY$pm7tOb*c#v4H@}34Hxue5)5FS%1Q?tKI9CTt+|^fZ)!Rvs;t9bA z*@*-gDro3}c;d#sxd_t>nJ&PH-UMQCa9dyFAvJL#?dGdx?)He)7g7@^(r&(5=HYiD z9!%X=i)C;+s&BNG&c5Yk#zJc1MB2?)%RG6*KkBQuWtWR^fj!ArFvV7aL~mcZ%)U*m zq#s0>tT6OItPNy=2T}I@0pFKzi`1;Vvm$7QAA-FcsJEMdEz#dsu7fG9N+)=RU%CtB zf~b-quJj$j>ifG)-1i%+ZwHh1J;&Px#1ZP1_K>z@;j|==^1poj53{Xr|MLBpm-jUxPa04Lb|GxVd59c>WSIb#2v_vQ zCkoshZH~P#I$>>bDon5H>%uKer8Kkf19=9;nF6QNH-hwa z-$o@z$ht^#>?E%|Z_noN1$|9q*|8Mr(n=Ip>olDHy1tl+VBFrfgcc%Vf#4fvA{b-) znhku%OaxPx#1ZP1_K>z@;j|==^1poj53{Xr|MLK~!i%?ULJ$A~6g^+2Q~Hd1Jfc zOX9f0JkVsskx-Y*9daaiJkR3se7ppX-Nuo=|2Fv$U-HU*w>>>D#i6GBBYC10lt5IwSQ(jJjLCd>u24 zJR=gx=n5G#GGC-YckOF8Q0ffx^*GHLi7&#ayY#j1r({=iu-6*t2?-5tZ9Oyl;@J>Q zW%gC9LbB*OBi&j~&j^#RM?5=gU)La7cAcSRJb|=0#FV~Kc*hs1(T7lWv~*klVtR+;B4@!@IBzdnsmW-%#%O_B$~!zCgAjZ2ReOJaXZS7BTqV!(a22 z8tJ2aFZufZf itemImages; private Texture backgroundImage; private Texture selectedBackgroundImage; + private Boolean showKeys = true; + private Texture interactKeyImage; private ShapeRenderer shapeRenderer; private Vector2 position; private Vector2 scale; @@ -37,12 +39,15 @@ public class InventoryDisplayHoverComponent extends RenderComponent { private static final float Y_OFFSET = 1.0F; private static final float SLOT_WIDTH = 0.6f; private static final float SLOT_HEIGHT = 0.6f; + private static final float KEY_WIDTH = 1.0f; + private static final float KEY_HEIGHT = 0.3f; @Override public void create() { super.create(); backgroundImage = new Texture("images/inventory_ui/item_background.png"); selectedBackgroundImage = new Texture("images/inventory_ui/item_background_selected.png"); + interactKeyImage = new Texture("images/inventory_ui/interact_key.png"); shapeRenderer = new ShapeRenderer(); ServiceLocator.getRenderService().register(this); @@ -81,6 +86,8 @@ private void updateImages() { } } + + /** * Updates this InventoryDisplay to reflect the current state of the InventoryComponent * of this component's parent entity. @@ -118,6 +125,15 @@ public void draw(SpriteBatch batch) { SLOT_WIDTH - 0.2f, SLOT_HEIGHT - 0.2f ); + + if (showKeys) { + batch.draw(interactKeyImage, + position.x, + position.y + 0.7f, + KEY_WIDTH, + KEY_HEIGHT + ); + } } } @@ -130,6 +146,11 @@ public void dispose() { } } + @Override + public int getLayer() { + return 3; // currently overlays the player, but decreasing this to 1 makes it hide behind the stations + } + @Override public void setStage(Stage mock) { // This function needed to exist but not needed to be implemented diff --git a/source/core/src/main/com/csse3200/game/entities/factories/StationFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/StationFactory.java index a2fa8fd4b..fc3f2fac8 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/StationFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/StationFactory.java @@ -411,7 +411,8 @@ public static Entity createTopBenchTable() { .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE)) .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) - .addComponent(new InventoryComponent(1)); + .addComponent(new InventoryComponent(1)) + .addComponent(new InventoryDisplayHoverComponent()); // Change this handler to the combining one //.addComponent(new StationItemHandlerComponent("benchTop", new ArrayList<>())); From f40f615a39b021f31691254d031160fc6fd2e90c Mon Sep 17 00:00:00 2001 From: s4809965 Date: Fri, 11 Oct 2024 00:54:03 +1000 Subject: [PATCH 02/14] #509 Made the keybind UI icons appear only when the player can interact with the station and added icons for rotate and combining items --- .../game/components/TooltipsDisplay.java | 2 +- .../InventoryDisplayHoverComponent.java | 51 ++++++++++++++++--- .../game/components/player/PlayerActions.java | 16 ++++++ .../station/StationProgressDisplay.java | 2 +- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/TooltipsDisplay.java b/source/core/src/main/com/csse3200/game/components/TooltipsDisplay.java index 4729a6f4c..f13fbbffe 100644 --- a/source/core/src/main/com/csse3200/game/components/TooltipsDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/TooltipsDisplay.java @@ -85,7 +85,7 @@ private void showTooltip(TooltipInfo tooltipInfo) { // Set the tooltip position above the interactable object based on viewport height table.setPosition(screenPos.x*190, screenPos.y*190 + (viewportHeight * 0.05f)); // Adjust Y position relative to object and screen size - table.setVisible(true); + table.setVisible(false); } private void hideTooltip() { diff --git a/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java b/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java index 9c9374e1d..c1f1ed8c2 100644 --- a/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java +++ b/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java @@ -12,7 +12,7 @@ import com.csse3200.game.components.items.ItemComponent; import java.util.ArrayList; import java.util.Objects; - +import com.csse3200.game.components.station.StationMealComponent; import com.csse3200.game.physics.components.PhysicsComponent; @@ -30,8 +30,11 @@ public class InventoryDisplayHoverComponent extends RenderComponent { private ArrayList itemImages; private Texture backgroundImage; private Texture selectedBackgroundImage; - private Boolean showKeys = true; + private boolean showKeys = false; + private boolean isMixingStation = false; private Texture interactKeyImage; + private Texture combineKeyImage; + private Texture rotateKeyImage; private ShapeRenderer shapeRenderer; private Vector2 position; private Vector2 scale; @@ -48,12 +51,20 @@ public void create() { backgroundImage = new Texture("images/inventory_ui/item_background.png"); selectedBackgroundImage = new Texture("images/inventory_ui/item_background_selected.png"); interactKeyImage = new Texture("images/inventory_ui/interact_key.png"); + combineKeyImage = new Texture("images/inventory_ui/combine_key.png"); + rotateKeyImage = new Texture("images/inventory_ui/rotate_key.png"); shapeRenderer = new ShapeRenderer(); ServiceLocator.getRenderService().register(this); if (entity != null) { // listener for when the InventoryComponent attached to this entity is updated entity.getEvents().addListener("updateInventory", this::update); + + entity.getEvents().addListener("showToolTip", this::showToolTip); + entity.getEvents().addListener("hideToolTip", this::hideToolTip); + + isMixingStation = entity.getComponent(StationMealComponent.class) != null; + // need to use the physics body position of the entity as // the regular getPosition() on stations does not return the correct position. position = entity.getComponent(PhysicsComponent.class).getBody().getPosition(); @@ -97,6 +108,20 @@ public void update() { updateImages(); } + /** + * Sets this component to display keybind tooltip icons + */ + private void showToolTip() { + this.showKeys = true; + } + + /** + * Sets this component to hide keybind tooltip icons + */ + private void hideToolTip() { + this.showKeys = false; + } + @Override public void draw(SpriteBatch batch) { if (entity == null || position == null || scale == null) @@ -125,15 +150,29 @@ public void draw(SpriteBatch batch) { SLOT_WIDTH - 0.2f, SLOT_HEIGHT - 0.2f ); - - if (showKeys) { - batch.draw(interactKeyImage, + } + if (showKeys) { + batch.draw(interactKeyImage, + position.x, + position.y + 0.7f, + KEY_WIDTH, + KEY_HEIGHT + ); + if (isMixingStation) { + batch.draw(rotateKeyImage, position.x, - position.y + 0.7f, + position.y + 0.4f, + KEY_WIDTH, + KEY_HEIGHT + ); + batch.draw(combineKeyImage, + position.x, + position.y + 0.1f, KEY_WIDTH, KEY_HEIGHT ); } + } } diff --git a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java index 5e36be868..b0454a702 100644 --- a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java +++ b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java @@ -28,6 +28,7 @@ public class PlayerActions extends Component { private SensorComponent interactionSensor; private InventoryComponent playerInventory; private InventoryDisplay displayInventory; + private Fixture oldInteractable; @Override public void create() { @@ -76,6 +77,19 @@ private void updateInteraction() { Vector2 objectPosition = interactable.getBody().getPosition(); // Get object position String interactionKey = "Press E"; String itemName = "to interact"; + + if (oldInteractable == null) { + oldInteractable = interactable; + } + + if (oldInteractable != null && oldInteractable.getBody().getPosition() != objectPosition) { + Entity oldStation = ((BodyUserData) oldInteractable.getBody().getUserData()).entity; + oldStation.getEvents().trigger("hideToolTip"); + + Entity station = ((BodyUserData) interactable.getBody().getUserData()).entity; + station.getEvents().trigger("showToolTip"); + oldInteractable = interactable; + } // Create a TooltipInfo object with the text and position TooltipsDisplay.TooltipInfo tooltipInfo = new TooltipsDisplay.TooltipInfo(interactionKey + " " + itemName, objectPosition); @@ -83,6 +97,7 @@ private void updateInteraction() { entity.getEvents().trigger("showTooltip", tooltipInfo); } else { + entity.getEvents().trigger("hideTooltip"); } } @@ -126,6 +141,7 @@ void interact(String type) { } // Logic for what interaction even to call on the station station.getEvents().trigger("Station Interaction", playerInventory, displayInventory, type); + } } diff --git a/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java b/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java index b652d5d0b..ebe1c4364 100644 --- a/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java @@ -94,7 +94,7 @@ public void update() { } private void resetBar() { - displayBar = false; + displayBar = true; barPercentage = 0.0f; } From 95a7843703a0c62711ff53470a49b1439a6577ce Mon Sep 17 00:00:00 2001 From: s4809965 Date: Fri, 11 Oct 2024 15:02:23 +1000 Subject: [PATCH 03/14] #509 Fixes an issue with InventoryDisplayHoverComponent that was causing lag when many items were being displayed to once --- .../components/player/InventoryDisplayHoverComponent.java | 6 +++++- .../game/components/station/StationProgressDisplay.java | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java b/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java index c1f1ed8c2..bf5bd8c58 100644 --- a/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java +++ b/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java @@ -58,7 +58,7 @@ public void create() { if (entity != null) { // listener for when the InventoryComponent attached to this entity is updated - entity.getEvents().addListener("updateInventory", this::update); + entity.getEvents().addListener("updateInventory", this::updateDisplay); entity.getEvents().addListener("showToolTip", this::showToolTip); entity.getEvents().addListener("hideToolTip", this::hideToolTip); @@ -105,6 +105,10 @@ private void updateImages() { */ @Override public void update() { + + } + + public void updateDisplay() { updateImages(); } diff --git a/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java b/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java index ebe1c4364..b652d5d0b 100644 --- a/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java @@ -94,7 +94,7 @@ public void update() { } private void resetBar() { - displayBar = true; + displayBar = false; barPercentage = 0.0f; } From 67d6e2faaaf41e2c2e1830c703d6acf9223ab3af Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 16:10:10 +1000 Subject: [PATCH 04/14] Fixed so that the Interaction service acts as more of a service and there is a new interaction component --- .../csse3200/game/areas/ForestGameArea.java | 5 ++ .../player/InteractionComponent2.java | 46 +++++++++++++++++++ .../game/components/player/PlayerActions.java | 35 +++----------- .../csse3200/game/entities/benches/Bench.java | 3 +- .../entities/factories/PlayerFactory.java | 8 ++-- .../game/services/InteractableService.java | 10 ++-- .../com/csse3200/game/services/MapLayout.java | 3 +- .../game/services/ServiceLocator.java | 10 ++++ 8 files changed, 81 insertions(+), 39 deletions(-) create mode 100644 source/core/src/main/com/csse3200/game/components/player/InteractionComponent2.java 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 503f18ceb..4359ab8a1 100644 --- a/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java +++ b/source/core/src/main/com/csse3200/game/areas/ForestGameArea.java @@ -12,6 +12,7 @@ import com.csse3200.game.components.moral.MoralDayFour; import com.csse3200.game.areas.map.Map; +import com.csse3200.game.services.InteractableService; import com.csse3200.game.services.MapLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -249,6 +250,10 @@ public void create() { // call load function based on the level argument // return list of items to spawn based on the load function // Baaed on lsit of items to spawn, spawn the items + + // Create a new interactable service + ServiceLocator.registerInteractableService(new InteractableService()); + loadAssets(); displayUI(); spawnTerrain(); diff --git a/source/core/src/main/com/csse3200/game/components/player/InteractionComponent2.java b/source/core/src/main/com/csse3200/game/components/player/InteractionComponent2.java new file mode 100644 index 000000000..d78707389 --- /dev/null +++ b/source/core/src/main/com/csse3200/game/components/player/InteractionComponent2.java @@ -0,0 +1,46 @@ +package com.csse3200.game.components.player; + +import java.util.Map; + +import com.badlogic.gdx.math.Vector2; +import com.csse3200.game.components.Component; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.physics.components.PhysicsComponent; +import com.csse3200.game.services.ServiceLocator; + +/** + * A component for the player that allows them to interact with stations and benches. + * This component is used to get the clostest entity to the player and interact with it. + */ +public class InteractionComponent2 extends Component{ + + Vector2 playerPosition; + Map interactables; + + @Override + public void create() { + playerPosition = entity.getComponent(PhysicsComponent.class).getBody().getPosition(); + interactables = ServiceLocator.getInteractableService().getInteractables(); + } + + public Entity getClosestInteractable() { + Entity closestEntity = null; + + float closestDistance = Float.MAX_VALUE; + + for (Map.Entry entry : interactables.entrySet()) { + Entity entity = entry.getKey(); + Vector2 entityPosition = entry.getValue(); + + float distance = playerPosition.dst(entityPosition); + + if (distance <= 1.15f && distance < closestDistance) { + closestDistance = distance; + closestEntity = entity; + } + } + + return closestEntity; + } + +} diff --git a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java index e3b1389aa..65c2d479b 100644 --- a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java +++ b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java @@ -28,14 +28,16 @@ public class PlayerActions extends Component { private PhysicsComponent physicsComponent; private Vector2 walkDirection = Vector2.Zero.cpy(); private boolean moving = false; - private SensorComponent interactionSensor; + //private SensorComponent interactionSensor; private InventoryComponent playerInventory; private InventoryDisplay displayInventory; + private InteractionComponent2 interactionComponent2; @Override public void create() { + interactionComponent2 = entity.getComponent(InteractionComponent2.class); physicsComponent = entity.getComponent(PhysicsComponent.class); - interactionSensor = entity.getComponent(SensorComponent.class); + //interactionSensor = entity.getComponent(SensorComponent.class); playerInventory = entity.getComponent(InventoryComponent.class); displayInventory = entity.getComponent(InventoryDisplay.class); entity.getEvents().addListener("walk", this::walk); @@ -73,7 +75,7 @@ public void update() { * the tooltip. * */ private void updateInteraction() { - interactionSensor.update(); + /*interactionSensor.update(); Fixture interactable = interactionSensor.getClosestFixture(); if (interactable != null) { Vector2 objectPosition = interactable.getBody().getPosition(); // Get object position @@ -87,7 +89,7 @@ private void updateInteraction() { } else { entity.getEvents().trigger("hideTooltip"); - } + }*/ } private void updateSpeed() { @@ -108,30 +110,7 @@ private void updateSpeed() { * Triggers an interaction event. It holds the logic in how to interact with a given station */ void interact(String type) { - Map interactables = InteractableService.getInteractables(); - - Entity closestEntity = null; - - // Get the player position - Vector2 playerPosition = entity.getComponent(PhysicsComponent.class).getBody().getPosition(); - float closestDistance = Float.MAX_VALUE; - - for (Map.Entry entry : interactables.entrySet()) { - Entity entity = entry.getKey(); - Vector2 entityPosition = entry.getValue(); - - float distance = playerPosition.dst(entityPosition); - - if (distance <= 1.15f && distance < closestDistance) { - closestDistance = distance; - closestEntity = entity; - } - } - - // If no station meets the criteria, return - if (closestEntity == null) { - return; - } + Entity closestEntity = interactionComponent2.getClosestInteractable(); closestEntity.getEvents().trigger("Station Interaction", playerInventory, displayInventory, type); } diff --git a/source/core/src/main/com/csse3200/game/entities/benches/Bench.java b/source/core/src/main/com/csse3200/game/entities/benches/Bench.java index be3792a86..014dadab6 100644 --- a/source/core/src/main/com/csse3200/game/entities/benches/Bench.java +++ b/source/core/src/main/com/csse3200/game/entities/benches/Bench.java @@ -15,6 +15,7 @@ import com.csse3200.game.physics.components.PhysicsComponent; import com.csse3200.game.rendering.TextureRenderComponent; import com.csse3200.game.services.InteractableService; +import com.csse3200.game.services.ServiceLocator; import com.csse3200.game.physics.components.InteractionComponent; import com.csse3200.game.components.TooltipsDisplay; import com.csse3200.game.components.player.InventoryComponent; @@ -59,7 +60,7 @@ public Bench(String type, int x, int y) { getComponent(PhysicsComponent.class).setBodyType(BodyType.StaticBody); PhysicsUtils.setScaledCollider(this, 1f, 0.75f); - InteractableService.registerEntity(this); + ServiceLocator.getInteractableService().registerEntity(this); } /** diff --git a/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java index 275b168d1..f762d0b19 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java @@ -6,6 +6,7 @@ import com.csse3200.game.components.SensorComponent; import com.csse3200.game.components.TooltipsDisplay; import com.csse3200.game.components.maingame.CheckWinLoseComponent; +import com.csse3200.game.components.player.InteractionComponent2; import com.csse3200.game.components.player.InventoryComponent; import com.csse3200.game.components.player.InventoryDisplay; import com.csse3200.game.components.player.PlayerActions; @@ -78,11 +79,10 @@ public static Entity createPlayer(PlayerConfig config) { .addComponent(inputComponent) .addComponent(animator) .addComponent(new PlayerAnimationController()) - .addComponent(new TooltipsDisplay()) .addComponent(new PlayerStatsDisplay()) - .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) - .addComponent(new CheckWinLoseComponent(55, 55)) - .addComponent(new SensorComponent(PhysicsLayer.INTERACTABLE, 5f)); + .addComponent(new InteractionComponent2()) + //.addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) + .addComponent(new CheckWinLoseComponent(55, 55)); player.scaleHeight(1.25f); PhysicsUtils.setScaledCollider(player, 0.5f, 0.5f); diff --git a/source/core/src/main/com/csse3200/game/services/InteractableService.java b/source/core/src/main/com/csse3200/game/services/InteractableService.java index d1dfd4707..9e9e0b398 100644 --- a/source/core/src/main/com/csse3200/game/services/InteractableService.java +++ b/source/core/src/main/com/csse3200/game/services/InteractableService.java @@ -17,13 +17,13 @@ public class InteractableService { // This assumes the position of the entity is static in the world - private static final Map interactables = new HashMap(); + private final Map interactables = new HashMap(); /** * Register an interactable enitity to the service * @param entity - the entity to register */ - public static void registerEntity(Entity entity) { + public void registerEntity(Entity entity) { PhysicsComponent physicsComponent = entity.getComponent(PhysicsComponent.class); if (physicsComponent == null) { @@ -37,14 +37,14 @@ public static void registerEntity(Entity entity) { * Remove the entity from the interactables map * @param entity to be removed */ - public static void unregisterEntity(Entity entity) { + public void unregisterEntity(Entity entity) { interactables.remove(entity); } /** * Clear all entities from the interactables map */ - public static void clearEntities() { + public void clearEntities() { interactables.clear(); } @@ -53,7 +53,7 @@ public static void clearEntities() { * entries in a mao * @return the interactables map */ - public static Map getInteractables() { + public Map getInteractables() { return interactables; } diff --git a/source/core/src/main/com/csse3200/game/services/MapLayout.java b/source/core/src/main/com/csse3200/game/services/MapLayout.java index f71e5a673..fcf966638 100644 --- a/source/core/src/main/com/csse3200/game/services/MapLayout.java +++ b/source/core/src/main/com/csse3200/game/services/MapLayout.java @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; import java.io.*; +import java.security.Provider.Service; import java.util.ArrayList; import java.util.Objects; @@ -191,7 +192,7 @@ public Entity readStation(String type, int col, int row) { } station.setPosition(col + 4, row - 4); - InteractableService.registerEntity(station); + ServiceLocator.getInteractableService().registerEntity(station); return station; } 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 a52541992..e3a00ce68 100644 --- a/source/core/src/main/com/csse3200/game/services/ServiceLocator.java +++ b/source/core/src/main/com/csse3200/game/services/ServiceLocator.java @@ -40,6 +40,7 @@ public class ServiceLocator { private static Cutscene currentCutscene; private static MapLayout map; private static MainMenuDisplay mainMenuDisplay; + private static InteractableService interactableService; private static ResourceService resourceService; @@ -98,6 +99,10 @@ public static DayNightService getDayNightService() { //new return dayNightService; } + public static InteractableService getInteractableService() { + return interactableService; + } + public static OrderActions getOrderActions() { @@ -241,6 +246,10 @@ public static void registerGameArea(GameArea game) { } + public static void registerInteractableService(InteractableService service) { + interactableService = service; + } + public static void registerMainMenuDisplay(MainMenuDisplay display) { mainMenuDisplay = display; @@ -301,6 +310,7 @@ public static void clear() { dayNightService = null; saveLoadService = null; randomComboService = null; + interactableService = null; } private ServiceLocator() { From 2a3923a2941d2d0a8e780cd57eabc5188c1a5856 Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 16:44:04 +1000 Subject: [PATCH 05/14] Removed interactionComponent2 and instead updated the sensor componet --- .../game/components/SensorComponent.java | 155 ++---------------- .../player/InteractionComponent2.java | 46 ------ .../game/components/player/PlayerActions.java | 9 +- .../entities/factories/PlayerFactory.java | 6 +- 4 files changed, 23 insertions(+), 193 deletions(-) delete mode 100644 source/core/src/main/com/csse3200/game/components/player/InteractionComponent2.java diff --git a/source/core/src/main/com/csse3200/game/components/SensorComponent.java b/source/core/src/main/com/csse3200/game/components/SensorComponent.java index 0a8ae9ffd..1280dbc39 100644 --- a/source/core/src/main/com/csse3200/game/components/SensorComponent.java +++ b/source/core/src/main/com/csse3200/game/components/SensorComponent.java @@ -1,162 +1,43 @@ package com.csse3200.game.components; -import com.badlogic.gdx.physics.box2d.Body; import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.physics.box2d.Fixture; import com.csse3200.game.entities.Entity; -import com.csse3200.game.physics.BodyUserData; -import com.csse3200.game.physics.PhysicsLayer; -import com.csse3200.game.physics.components.InteractionComponent; import com.csse3200.game.physics.components.PhysicsComponent; -import com.csse3200.game.utils.math.Vector2Utils; +import com.csse3200.game.services.ServiceLocator; -import java.util.HashSet; -import java.util.Set; -import java.util.Vector; +import java.util.Map; /** * Finds the closest interactable object inside a range of the player */ public class SensorComponent extends Component { - private final short targetLayer; - private final float sensorDistance; - private InteractionComponent interactionComponent; - private Set collidingFixtures = new HashSet<>(); - private Fixture closestFixture = null; - private float closestDistance = -1f; - - /** - * Create a component that senses entities when it collides with them. - * @param targetLayer The physics layer of the target's collider - */ - public SensorComponent(short targetLayer, float sensorDistance) { - this.targetLayer = targetLayer; - this.sensorDistance = sensorDistance; - } + Vector2 playerPosition; + Map interactables; @Override public void create() { - entity.getEvents().addListener("collisionStart", this::onCollisionStart); - entity.getEvents().addListener("collisionEnd", this::onCollisionEnd); - this.interactionComponent = entity.getComponent(InteractionComponent.class); - super.create(); - } - - /** - * Getters - */ - - public InteractionComponent getInteractionComponent() { - return this.interactionComponent; - } - - public short getTargetLayer() { - return this.targetLayer; + playerPosition = entity.getComponent(PhysicsComponent.class).getBody().getPosition(); + interactables = ServiceLocator.getInteractableService().getInteractables(); } + + public Entity getClosestInteractable() { + Entity closestEntity = null; - public Set getClosestFixtures() { - return this.collidingFixtures; - } + float closestDistance = Float.MAX_VALUE; - public int getNumFixtures() { - return this.collidingFixtures.size(); - } + for (Map.Entry entry : interactables.entrySet()) { + Entity entity = entry.getKey(); + Vector2 entityPosition = entry.getValue(); - /** - * Called when the component collides with another collider - * @param me Should be the fixture that got collided with - aka this sensor component - * @param other The fixture that collided with this component - */ - public void onCollisionStart(Fixture me, Fixture other) { - if (interactionComponent.getFixture() != me) { - // Not triggered by me, so ignore - return; - } - // Check that the fixture has the correct target layer - if (!PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits)) { - // Doesn't match our target layer, ignore - return; - } - // Check if the collidingFixture is close enough sensor - if (isWithinDistance(other, sensorDistance)) - { - // Update the collision set - collidingFixtures.add(other); - } - //Update the set of fixtures - updateFixtures(); - } + float distance = playerPosition.dst(entityPosition); - /** - * Called when a collision has ended between 2 fixtures - * @param me Should be the fixture that got collided with - aka this sensor component - * @param other The fixture that this component stopped colliding with - */ - public void onCollisionEnd(Fixture me, Fixture other) { - if (interactionComponent.getFixture() != me) { - // Not triggered by interactionComponent, so ignore - return; - } - if (!PhysicsLayer.contains(targetLayer, other.getFilterData().categoryBits)) { - // Doesn't match our target layer, ignore - return; - } - // Remove the fixture if it was previously detected - collidingFixtures.remove(other); - //Update the set of fixtures - updateFixtures(); - } - - /** - * Removes any fixtures that are not in range of the sensor and updates the closest fixture - */ - private void updateFixtures() { - Set toRemove = new HashSet<>(); - Fixture previousClosestFixture = closestFixture; - for (Fixture fixture : collidingFixtures) { - float dist = getFixtureDistance(fixture); - if (dist > sensorDistance) { - toRemove.add(fixture); - } else if (closestDistance < 0 || dist < closestDistance) { - closestDistance = dist; - closestFixture = fixture; + if (distance <= 1.15f && distance < closestDistance) { + closestDistance = distance; + closestEntity = entity; } } - collidingFixtures.removeAll(toRemove); - - // If no colliding fixtures, then it is empty - if (collidingFixtures.isEmpty()) { - closestFixture = null; - closestDistance = -1f; - } - - - } - - - public Fixture getClosestFixture() { - return this.closestFixture; - } - - /** - * Returns the distance from the sensor component to the given fixture - * @param fixture The object being measured - * @return The distance from the sensor object to the given fixture - */ - private float getFixtureDistance(Fixture fixture) { - Vector2 sensorPosition = interactionComponent.getFixture().getBody().getPosition(); - Vector2 fixturePosition = fixture.getBody().getPosition(); - return sensorPosition.dst(fixturePosition); - } - /** - * Determines whether the distance to the fixture is within the required distance - * @param fixture The object being measured - * @param distance The maximum distance the fixture can be away from the sensor component - * @return True: If the distance to the fixture is less than the provided distance, False: Otherwise - */ - public boolean isWithinDistance(Fixture fixture, float distance) { - return getFixtureDistance(fixture) <= distance; + return closestEntity; } } diff --git a/source/core/src/main/com/csse3200/game/components/player/InteractionComponent2.java b/source/core/src/main/com/csse3200/game/components/player/InteractionComponent2.java deleted file mode 100644 index d78707389..000000000 --- a/source/core/src/main/com/csse3200/game/components/player/InteractionComponent2.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.csse3200.game.components.player; - -import java.util.Map; - -import com.badlogic.gdx.math.Vector2; -import com.csse3200.game.components.Component; -import com.csse3200.game.entities.Entity; -import com.csse3200.game.physics.components.PhysicsComponent; -import com.csse3200.game.services.ServiceLocator; - -/** - * A component for the player that allows them to interact with stations and benches. - * This component is used to get the clostest entity to the player and interact with it. - */ -public class InteractionComponent2 extends Component{ - - Vector2 playerPosition; - Map interactables; - - @Override - public void create() { - playerPosition = entity.getComponent(PhysicsComponent.class).getBody().getPosition(); - interactables = ServiceLocator.getInteractableService().getInteractables(); - } - - public Entity getClosestInteractable() { - Entity closestEntity = null; - - float closestDistance = Float.MAX_VALUE; - - for (Map.Entry entry : interactables.entrySet()) { - Entity entity = entry.getKey(); - Vector2 entityPosition = entry.getValue(); - - float distance = playerPosition.dst(entityPosition); - - if (distance <= 1.15f && distance < closestDistance) { - closestDistance = distance; - closestEntity = entity; - } - } - - return closestEntity; - } - -} diff --git a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java index 65c2d479b..cc7d3c5a2 100644 --- a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java +++ b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java @@ -12,6 +12,7 @@ import com.csse3200.game.components.items.PlateComponent; import com.csse3200.game.components.station.FireExtinguisherHandlerComponent; import com.csse3200.game.components.TooltipsDisplay; +import com.csse3200.game.physics.components.InteractionComponent; import com.csse3200.game.physics.components.PhysicsComponent; import com.csse3200.game.services.InteractableService; import com.csse3200.game.services.ServiceLocator; @@ -28,16 +29,14 @@ public class PlayerActions extends Component { private PhysicsComponent physicsComponent; private Vector2 walkDirection = Vector2.Zero.cpy(); private boolean moving = false; - //private SensorComponent interactionSensor; + private SensorComponent sensor; private InventoryComponent playerInventory; private InventoryDisplay displayInventory; - private InteractionComponent2 interactionComponent2; @Override public void create() { - interactionComponent2 = entity.getComponent(InteractionComponent2.class); physicsComponent = entity.getComponent(PhysicsComponent.class); - //interactionSensor = entity.getComponent(SensorComponent.class); + sensor = entity.getComponent(SensorComponent.class); playerInventory = entity.getComponent(InventoryComponent.class); displayInventory = entity.getComponent(InventoryDisplay.class); entity.getEvents().addListener("walk", this::walk); @@ -110,7 +109,7 @@ private void updateSpeed() { * Triggers an interaction event. It holds the logic in how to interact with a given station */ void interact(String type) { - Entity closestEntity = interactionComponent2.getClosestInteractable(); + Entity closestEntity = sensor.getClosestInteractable(); closestEntity.getEvents().trigger("Station Interaction", playerInventory, displayInventory, type); } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java index f762d0b19..a57af84a7 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/PlayerFactory.java @@ -4,9 +4,7 @@ import com.badlogic.gdx.math.Vector2; import com.csse3200.game.components.CombatStatsComponent; import com.csse3200.game.components.SensorComponent; -import com.csse3200.game.components.TooltipsDisplay; import com.csse3200.game.components.maingame.CheckWinLoseComponent; -import com.csse3200.game.components.player.InteractionComponent2; import com.csse3200.game.components.player.InventoryComponent; import com.csse3200.game.components.player.InventoryDisplay; import com.csse3200.game.components.player.PlayerActions; @@ -21,7 +19,6 @@ import com.csse3200.game.physics.PhysicsUtils; import com.csse3200.game.physics.components.ColliderComponent; import com.csse3200.game.physics.components.HitboxComponent; -import com.csse3200.game.physics.components.InteractionComponent; import com.csse3200.game.physics.components.PhysicsComponent; import com.csse3200.game.rendering.AnimationRenderComponent; import com.csse3200.game.services.ServiceLocator; @@ -80,8 +77,7 @@ public static Entity createPlayer(PlayerConfig config) { .addComponent(animator) .addComponent(new PlayerAnimationController()) .addComponent(new PlayerStatsDisplay()) - .addComponent(new InteractionComponent2()) - //.addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) + .addComponent(new SensorComponent()) .addComponent(new CheckWinLoseComponent(55, 55)); player.scaleHeight(1.25f); From cca2db7570892131771293df06a776b6787ff690 Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 16:52:43 +1000 Subject: [PATCH 06/14] Fixed testing of the new sensor component --- .../CustomerSensorComponentTest.java | 303 +++++++++--------- .../interaction/InteractionComponentTest.java | 110 +++---- .../interaction/SensorComponentTest.java | 7 +- 3 files changed, 210 insertions(+), 210 deletions(-) diff --git a/source/core/src/test/com/csse3200/game/components/interaction/CustomerSensorComponentTest.java b/source/core/src/test/com/csse3200/game/components/interaction/CustomerSensorComponentTest.java index e89e862db..8a58ebf7b 100644 --- a/source/core/src/test/com/csse3200/game/components/interaction/CustomerSensorComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/interaction/CustomerSensorComponentTest.java @@ -1,152 +1,151 @@ -import com.csse3200.game.components.CustomerSensorComponent; -import com.csse3200.game.components.npc.CustomerComponent; - -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.physics.box2d.Fixture; -import com.csse3200.game.entities.Entity; -import com.csse3200.game.entities.configs.BaseCustomerConfig; -import com.csse3200.game.extensions.GameExtension; -import com.csse3200.game.physics.PhysicsService; -import com.csse3200.game.physics.PhysicsLayer; -import com.csse3200.game.physics.components.PhysicsComponent; -import com.csse3200.game.physics.components.InteractionComponent; -import com.csse3200.game.services.ServiceLocator; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - - -import static org.junit.jupiter.api.Assertions.*; - -@ExtendWith(GameExtension.class) -class CustomerSensorComponentTest { - - private CustomerSensorComponent customerSensorComponent; - private final short customerLayer = PhysicsLayer.NPC; - - @BeforeEach - void beforeEach() { - ServiceLocator.registerPhysicsService(new PhysicsService()); - customerSensorComponent = new CustomerSensorComponent(customerLayer, 5f); - assertNotNull(customerSensorComponent, "CustomerSensorComponent should have been initialized"); - } - - @Test - void checkSensorInit() { - assertNotNull(this.customerSensorComponent, "The customerSensorComponent should not be null"); - - // Set up entities - Entity player = createEntity(0, 0); - Entity customer = createCustomer(1, 1); - - Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); - Fixture customerFixture = customer.getComponent(InteractionComponent.class).getFixture(); - - InteractionComponent sensorInteractionComponent = this.customerSensorComponent.getInteractionComponent(); - assertNotNull(sensorInteractionComponent, "The interaction component for sensor should not be null"); - - Fixture sensorFixture = sensorInteractionComponent.getFixture(); - assertNotNull(sensorFixture, "The fixture for interaction component of sensor should not be null"); - - assertSame(sensorFixture, playerFixture, "Sensor fixture should match player fixture"); - } - - @Test - void shouldDetectClosestCustomer() { - Entity player = createEntity(0, 0); - Entity customer = createCustomer(1, 1); - - Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); - Fixture customerFixture = customer.getComponent(InteractionComponent.class).getFixture(); - - customerSensorComponent.onCollisionStart(playerFixture, customerFixture); - - Fixture closestFixture = customerSensorComponent.getClosestCustomer(); - assertNotNull(closestFixture, "Closest fixture should not be null"); - assertEquals(customerFixture, closestFixture, "The closest fixture should be the customer's fixture"); - } - - @Test - void shouldNotDetectFarAwayCustomer() { - Entity player = createEntity(0, 0); - Entity customer = createCustomer(10, 10); - - Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); - Fixture customerFixture = customer.getComponent(InteractionComponent.class).getFixture(); - - customerSensorComponent.onCollisionStart(playerFixture, customerFixture); - assertFalse(customerSensorComponent.isWithinDistance(customerFixture, 5), "Customer should be too far away"); - - Fixture closestFixture = customerSensorComponent.getClosestCustomer(); - assertNull(closestFixture, "Closest fixture should be null because the customer is too far"); - } - - @Test - void shouldNotDetectNonCustomerEntity() { - Entity player = createEntity(0, 0); - Entity nonCustomer = createNonCustomer(1, 1); - - Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); - Fixture nonCustomerFixture = nonCustomer.getComponent(InteractionComponent.class).getFixture(); - - customerSensorComponent.onCollisionStart(playerFixture, nonCustomerFixture); - - Fixture closestFixture = customerSensorComponent.getClosestCustomer(); - assertNull(closestFixture, "Closest fixture should be null because the entity is not a customer"); - } - - @Test - void shouldRemoveCustomerAfterCollisionEnded() { - Entity player = createEntity(0, 0); - Entity customer = createCustomer(1, 1); - - Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); - Fixture customerFixture = customer.getComponent(InteractionComponent.class).getFixture(); - - customerSensorComponent.onCollisionStart(playerFixture, customerFixture); - - Fixture closestFixture = customerSensorComponent.getClosestCustomer(); - assertNotNull(closestFixture, "Closest fixture should not be null"); - assertEquals(customerFixture, closestFixture, "The closest fixture should be the customer's fixture"); - - // Move the player out of collision range - player.setPosition(20, 20); - customerSensorComponent.onCollisionEnd(playerFixture, customerFixture); - - closestFixture = customerSensorComponent.getClosestCustomer(); - assertNull(closestFixture, "Closest fixture should now be null after collision ends"); - } - - private Entity createEntity(float x, float y) { - Entity entity = new Entity(); - entity.setPosition(x, y); - entity.addComponent(new PhysicsComponent()); - entity.addComponent(customerSensorComponent); - InteractionComponent component = new InteractionComponent(PhysicsLayer.PLAYER); - entity.addComponent(component); - entity.create(); - return entity; - } - - private Entity createCustomer(float x, float y) { - Entity customer = new Entity(); - customer.setPosition(x, y); - BaseCustomerConfig baseConfig = new BaseCustomerConfig(); - customer.addComponent(new CustomerComponent(baseConfig)); // Add CustomerComponent to identify as customer - InteractionComponent interactionComponent = new InteractionComponent(PhysicsLayer.NPC); - customer.addComponent(new PhysicsComponent()); - customer.addComponent(interactionComponent); - customer.create(); - return customer; - } - - private Entity createNonCustomer(float x, float y) { - Entity nonCustomer = new Entity(); - nonCustomer.setPosition(x, y); - InteractionComponent interactionComponent = new InteractionComponent(PhysicsLayer.PLAYER); // Non-customer layer - nonCustomer.addComponent(new PhysicsComponent()); - nonCustomer.addComponent(interactionComponent); - nonCustomer.create(); - return nonCustomer; - } -} +package com.csse3200.game.components.interaction; + +import com.csse3200.game.components.CustomerSensorComponent; +import com.csse3200.game.components.npc.CustomerComponent; + +import com.badlogic.gdx.physics.box2d.Fixture; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.entities.configs.BaseCustomerConfig; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.components.PhysicsComponent; +import com.csse3200.game.physics.components.InteractionComponent; +import com.csse3200.game.services.ServiceLocator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(GameExtension.class) +class CustomerSensorComponentTest { + + private CustomerSensorComponent customerSensorComponent; + private final short customerLayer = PhysicsLayer.NPC; + + @BeforeEach + void beforeEach() { + ServiceLocator.registerPhysicsService(new PhysicsService()); + customerSensorComponent = new CustomerSensorComponent(customerLayer, 5f); + assertNotNull(customerSensorComponent, "CustomerSensorComponent should have been initialized"); + } + + @Test + void checkSensorInit() { + assertNotNull(this.customerSensorComponent, "The customerSensorComponent should not be null"); + + // Set up entities + Entity player = createEntity(0, 0); + + Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); + + InteractionComponent sensorInteractionComponent = this.customerSensorComponent.getInteractionComponent(); + assertNotNull(sensorInteractionComponent, "The interaction component for sensor should not be null"); + + Fixture sensorFixture = sensorInteractionComponent.getFixture(); + assertNotNull(sensorFixture, "The fixture for interaction component of sensor should not be null"); + + assertSame(sensorFixture, playerFixture, "Sensor fixture should match player fixture"); + } + + @Test + void shouldDetectClosestCustomer() { + Entity player = createEntity(0, 0); + Entity customer = createCustomer(1, 1); + + Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); + Fixture customerFixture = customer.getComponent(InteractionComponent.class).getFixture(); + + customerSensorComponent.onCollisionStart(playerFixture, customerFixture); + + Fixture closestFixture = customerSensorComponent.getClosestCustomer(); + assertNotNull(closestFixture, "Closest fixture should not be null"); + assertEquals(customerFixture, closestFixture, "The closest fixture should be the customer's fixture"); + } + + @Test + void shouldNotDetectFarAwayCustomer() { + Entity player = createEntity(0, 0); + Entity customer = createCustomer(10, 10); + + Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); + Fixture customerFixture = customer.getComponent(InteractionComponent.class).getFixture(); + + customerSensorComponent.onCollisionStart(playerFixture, customerFixture); + assertFalse(customerSensorComponent.isWithinDistance(customerFixture, 5), "Customer should be too far away"); + + Fixture closestFixture = customerSensorComponent.getClosestCustomer(); + assertNull(closestFixture, "Closest fixture should be null because the customer is too far"); + } + + @Test + void shouldNotDetectNonCustomerEntity() { + Entity player = createEntity(0, 0); + Entity nonCustomer = createNonCustomer(1, 1); + + Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); + Fixture nonCustomerFixture = nonCustomer.getComponent(InteractionComponent.class).getFixture(); + + customerSensorComponent.onCollisionStart(playerFixture, nonCustomerFixture); + + Fixture closestFixture = customerSensorComponent.getClosestCustomer(); + assertNull(closestFixture, "Closest fixture should be null because the entity is not a customer"); + } + + @Test + void shouldRemoveCustomerAfterCollisionEnded() { + Entity player = createEntity(0, 0); + Entity customer = createCustomer(1, 1); + + Fixture playerFixture = player.getComponent(InteractionComponent.class).getFixture(); + Fixture customerFixture = customer.getComponent(InteractionComponent.class).getFixture(); + + customerSensorComponent.onCollisionStart(playerFixture, customerFixture); + + Fixture closestFixture = customerSensorComponent.getClosestCustomer(); + assertNotNull(closestFixture, "Closest fixture should not be null"); + assertEquals(customerFixture, closestFixture, "The closest fixture should be the customer's fixture"); + + // Move the player out of collision range + player.setPosition(20, 20); + customerSensorComponent.onCollisionEnd(playerFixture, customerFixture); + + closestFixture = customerSensorComponent.getClosestCustomer(); + assertNull(closestFixture, "Closest fixture should now be null after collision ends"); + } + + private Entity createEntity(float x, float y) { + Entity entity = new Entity(); + entity.setPosition(x, y); + entity.addComponent(new PhysicsComponent()); + entity.addComponent(customerSensorComponent); + InteractionComponent component = new InteractionComponent(PhysicsLayer.PLAYER); + entity.addComponent(component); + entity.create(); + return entity; + } + + private Entity createCustomer(float x, float y) { + Entity customer = new Entity(); + customer.setPosition(x, y); + BaseCustomerConfig baseConfig = new BaseCustomerConfig(); + customer.addComponent(new CustomerComponent(baseConfig)); // Add CustomerComponent to identify as customer + InteractionComponent interactionComponent = new InteractionComponent(PhysicsLayer.NPC); + customer.addComponent(new PhysicsComponent()); + customer.addComponent(interactionComponent); + customer.create(); + return customer; + } + + private Entity createNonCustomer(float x, float y) { + Entity nonCustomer = new Entity(); + nonCustomer.setPosition(x, y); + InteractionComponent interactionComponent = new InteractionComponent(PhysicsLayer.PLAYER); // Non-customer layer + nonCustomer.addComponent(new PhysicsComponent()); + nonCustomer.addComponent(interactionComponent); + nonCustomer.create(); + return nonCustomer; + } +} diff --git a/source/core/src/test/com/csse3200/game/components/interaction/InteractionComponentTest.java b/source/core/src/test/com/csse3200/game/components/interaction/InteractionComponentTest.java index dd3db9685..73b55ff13 100644 --- a/source/core/src/test/com/csse3200/game/components/interaction/InteractionComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/interaction/InteractionComponentTest.java @@ -1,56 +1,56 @@ -package com.csse3200.game.components; - -import com.badlogic.gdx.physics.box2d.Fixture; -import com.csse3200.game.entities.Entity; -import com.csse3200.game.extensions.GameExtension; -import com.csse3200.game.physics.PhysicsService; -import com.csse3200.game.physics.PhysicsLayer; -import com.csse3200.game.physics.components.PhysicsComponent; -import com.csse3200.game.physics.components.InteractionComponent; -import com.csse3200.game.services.ServiceLocator; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import static org.junit.jupiter.api.Assertions.*; - -@ExtendWith(GameExtension.class) -class InteractionComponentTest { - @BeforeEach - void beforeEach() { - ServiceLocator.registerPhysicsService(new PhysicsService()); - } - - @Test - public void shouldBeSensor() { - Entity entity = createEntity(); - InteractionComponent component = entity.getComponent(InteractionComponent.class); - - assertTrue(component.getFixture().isSensor()); - } - - @Test - public void shouldBeInteractable() { - Entity entity = createEntity(); - InteractionComponent component = entity.getComponent(InteractionComponent.class); - short interactableLayer = PhysicsLayer.INTERACTABLE; - assertTrue(component.getLayer() == interactableLayer); - assertTrue(PhysicsLayer.contains(interactableLayer, component.getFixture().getFilterData().categoryBits)); - } - - @Test - public void shouldHaveFixture() { - Entity entity = createEntity(); - InteractionComponent component = entity.getComponent(InteractionComponent.class); - assertNotNull(component.getFixture()); - } - - Entity createEntity() { - Entity entity = new Entity(); - entity.addComponent(new PhysicsComponent()); - InteractionComponent component = new InteractionComponent(PhysicsLayer.INTERACTABLE); - entity.addComponent(component); - entity.create(); - return entity; - } +package com.csse3200.game.components.interaction; + +import com.badlogic.gdx.physics.box2d.Fixture; +import com.csse3200.game.entities.Entity; +import com.csse3200.game.extensions.GameExtension; +import com.csse3200.game.physics.PhysicsService; +import com.csse3200.game.physics.PhysicsLayer; +import com.csse3200.game.physics.components.PhysicsComponent; +import com.csse3200.game.physics.components.InteractionComponent; +import com.csse3200.game.services.ServiceLocator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(GameExtension.class) +class InteractionComponentTest { + @BeforeEach + void beforeEach() { + ServiceLocator.registerPhysicsService(new PhysicsService()); + } + + @Test + public void shouldBeSensor() { + Entity entity = createEntity(); + InteractionComponent component = entity.getComponent(InteractionComponent.class); + + assertTrue(component.getFixture().isSensor()); + } + + @Test + public void shouldBeInteractable() { + Entity entity = createEntity(); + InteractionComponent component = entity.getComponent(InteractionComponent.class); + short interactableLayer = PhysicsLayer.INTERACTABLE; + assertTrue(component.getLayer() == interactableLayer); + assertTrue(PhysicsLayer.contains(interactableLayer, component.getFixture().getFilterData().categoryBits)); + } + + @Test + public void shouldHaveFixture() { + Entity entity = createEntity(); + InteractionComponent component = entity.getComponent(InteractionComponent.class); + assertNotNull(component.getFixture()); + } + + Entity createEntity() { + Entity entity = new Entity(); + entity.addComponent(new PhysicsComponent()); + InteractionComponent component = new InteractionComponent(PhysicsLayer.INTERACTABLE); + entity.addComponent(component); + entity.create(); + return entity; + } } \ No newline at end of file diff --git a/source/core/src/test/com/csse3200/game/components/interaction/SensorComponentTest.java b/source/core/src/test/com/csse3200/game/components/interaction/SensorComponentTest.java index 63b216bcd..75bd8eaee 100644 --- a/source/core/src/test/com/csse3200/game/components/interaction/SensorComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/interaction/SensorComponentTest.java @@ -1,4 +1,4 @@ -package com.csse3200.game.components; +package com.csse3200.game.components.interaction; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.Fixture; @@ -22,10 +22,11 @@ class SensorComponentTest { private SensorComponent sensorComponent; // Declare sensorComponent here private final short interactableLayer = PhysicsLayer.INTERACTABLE; + /*/ @BeforeEach void beforeEach() { ServiceLocator.registerPhysicsService(new PhysicsService()); - sensorComponent = new SensorComponent(interactableLayer, 1f); + sensorComponent = new SensorComponent(); assertNotNull(sensorComponent, "sensor component should have been initialised"); } @@ -270,5 +271,5 @@ void shouldIgnoreRemovedFixtureAfterCollisionEnd() { // Ensure the target is removed from the sensor's list assertEquals(0, sensorComponent.getNumFixtures(), "The fixture should be removed after the collision ends"); } - + */ } From aa722715640f9c7beb3cc591a322872e87cc9f7e Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 16:58:49 +1000 Subject: [PATCH 07/14] Fixed testing of the new sensor component --- .../csse3200/game/services/MapLayoutTest.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/source/core/src/test/com/csse3200/game/services/MapLayoutTest.java b/source/core/src/test/com/csse3200/game/services/MapLayoutTest.java index 9ee403aad..351eed1f8 100644 --- a/source/core/src/test/com/csse3200/game/services/MapLayoutTest.java +++ b/source/core/src/test/com/csse3200/game/services/MapLayoutTest.java @@ -1,28 +1,14 @@ package com.csse3200.game.services; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.assets.AssetManager; -import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.scenes.scene2d.Stage; -import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.viewport.Viewport; import com.csse3200.game.GdxGame; -import com.csse3200.game.areas.ForestGameArea; -import com.csse3200.game.areas.map.BenchGenerator; import com.csse3200.game.areas.terrain.TerrainFactory; import com.csse3200.game.entities.Entity; import com.csse3200.game.areas.map.Map; -import com.csse3200.game.entities.EntityService; import com.csse3200.game.entities.benches.Bench; -import com.csse3200.game.events.EventHandler; import com.csse3200.game.events.listeners.EventListener0; -import com.csse3200.game.events.listeners.EventListener1; import com.csse3200.game.extensions.GameExtension; -import com.csse3200.game.physics.PhysicsService; -import com.csse3200.game.rendering.RenderService; -import com.csse3200.game.rendering.Renderable; -import com.csse3200.game.screens.MainGameScreen; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -30,8 +16,6 @@ import org.mockito.*; import org.mockito.junit.jupiter.MockitoExtension; -import java.io.BufferedReader; -import java.io.StringReader; import java.util.ArrayList; import static org.junit.jupiter.api.Assertions.*; @@ -79,6 +63,7 @@ void setUp() { mapLayoutSpy = spy(new MapLayout()); ServiceLocator.registerMapLayout(mapLayoutSpy); + ServiceLocator.registerInteractableService(new InteractableService()); } From bb301d8b0e9bcbaa1d9ed730615151791078be13 Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 17:50:19 +1000 Subject: [PATCH 08/14] Fixed station progress bar not showing --- .../station/StationProgressDisplay.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java b/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java index b652d5d0b..b719abdb3 100644 --- a/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java @@ -61,11 +61,6 @@ public void create() { @Override public void update() { - super.update(); - if (entity == null) { - return; - } - ItemComponent item = entity.getComponent(StationItemHandlerComponent.class).peek(); if (item == null) { resetBar(); @@ -74,23 +69,27 @@ public void update() { ItemTimerComponent timerItem = item.getEntity().getComponent(ChopIngredientComponent.class); if (timerItem == null) { + // could not find a chop component, check if theres a cook component timerItem = item.getEntity().getComponent(CookIngredientComponent.class); } - if (timerItem == null) { resetBar(); return; } barPercentage = timerItem.getCompletionPercent() / 100; - displayBar = barPercentage < 1.0f || (timerItem instanceof CookIngredientComponent - && ((CookIngredientComponent) timerItem).getIsCooking()); - - if (barPercentage >= 1.0f) { + logger.info(String.valueOf(barPercentage)); + if (barPercentage < 1.0f) { + // if completion percent is less than 100%, then display the + // progress bar + displayBar = true; + } else if (barPercentage >= 1.0f && timerItem instanceof CookIngredientComponent + && ((CookIngredientComponent) timerItem).getIsCooking()) { barPercentage = 1.0f; + displayBar = true; + } else { + displayBar = false; } - - logger.info(String.valueOf(barPercentage)); } private void resetBar() { @@ -101,8 +100,9 @@ private void resetBar() { @Override public void draw(SpriteBatch batch) { - if (entity == null || position == null || scale == null) + if (entity == null || position == null || scale == null) { return; + } if (displayBar) { batch.draw(barOutline, position.x + X_OFFSET, @@ -127,7 +127,7 @@ public void dispose() { @Override public int getLayer() { - return 2; // currently overlays the player, but decreasing this to 1 makes it hide behind the stations + return 3; // currently overlays the player, but decreasing this to 1 makes it hide behind the stations } @Override From 1bc537833a90b39f5e7cbcfe24ded414ed2cac01 Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 22:21:05 +1000 Subject: [PATCH 09/14] Added tooltips display to the player actions --- .../game/components/player/PlayerActions.java | 84 +++++++------------ 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java index fafc5810b..0fad3254a 100644 --- a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java +++ b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java @@ -1,20 +1,11 @@ package com.csse3200.game.components.player; -import java.util.Map; - import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.physics.box2d.Body; -import com.badlogic.gdx.physics.box2d.Fixture; import com.csse3200.game.components.Component; import com.csse3200.game.entities.Entity; -import com.csse3200.game.physics.BodyUserData; -import com.csse3200.game.components.items.PlateComponent; -import com.csse3200.game.components.station.FireExtinguisherHandlerComponent; -import com.csse3200.game.components.TooltipsDisplay; -import com.csse3200.game.physics.components.InteractionComponent; import com.csse3200.game.physics.components.PhysicsComponent; -import com.csse3200.game.services.InteractableService; import com.csse3200.game.services.ServiceLocator; import com.csse3200.game.components.SensorComponent; @@ -32,7 +23,8 @@ public class PlayerActions extends Component { private SensorComponent sensor; private InventoryComponent playerInventory; private InventoryDisplay displayInventory; - private Fixture oldInteractable; + private Entity closestEntity = null; + private Entity oldClosestEntity = null; @Override public void create() { @@ -52,20 +44,21 @@ public void update() { Vector2 position = body.getPosition(); if (moving) { - // Stop if it's at min x position or max x position - if (position.x < MIN_X_POSITION) { - position.x = MIN_X_POSITION; - body.setTransform(MIN_X_POSITION, position.y, body.getAngle()); - stopWalking(); - } else if (position.x > MAX_X_POSITION) { - position.x = MAX_X_POSITION; - body.setTransform(MAX_X_POSITION, position.y, body.getAngle()); - stopWalking(); - } else { - updateSpeed(); - } + updateInteraction(); + + // Stop if it's at min x position or max x position + if (position.x < MIN_X_POSITION) { + position.x = MIN_X_POSITION; + body.setTransform(MIN_X_POSITION, position.y, body.getAngle()); + stopWalking(); + } else if (position.x > MAX_X_POSITION) { + position.x = MAX_X_POSITION; + body.setTransform(MAX_X_POSITION, position.y, body.getAngle()); + stopWalking(); + } else { + updateSpeed(); + } } - updateInteraction(); } /** @@ -75,35 +68,20 @@ public void update() { * the tooltip. * */ private void updateInteraction() { - /*interactionSensor.update(); - Fixture interactable = interactionSensor.getClosestFixture(); - if (interactable != null) { - Vector2 objectPosition = interactable.getBody().getPosition(); // Get object position - String interactionKey = "Press E"; - String itemName = "to interact"; - - if (oldInteractable == null) { - oldInteractable = interactable; - } - - if (oldInteractable != null && oldInteractable.getBody().getPosition() != objectPosition) { - Entity oldStation = ((BodyUserData) oldInteractable.getBody().getUserData()).entity; - oldStation.getEvents().trigger("hideToolTip"); - - Entity station = ((BodyUserData) interactable.getBody().getUserData()).entity; - station.getEvents().trigger("showToolTip"); - oldInteractable = interactable; - } - // Create a TooltipInfo object with the text and position - TooltipsDisplay.TooltipInfo tooltipInfo = new TooltipsDisplay.TooltipInfo(interactionKey + " " + itemName, objectPosition); - - // Trigger the event with the TooltipInfo object - entity.getEvents().trigger("showTooltip", tooltipInfo); - - } else { - - entity.getEvents().trigger("hideTooltip"); - }*/ + oldClosestEntity = closestEntity; + closestEntity = sensor.getClosestInteractable(); + + if (oldClosestEntity == closestEntity) { + return; + } + + if (oldClosestEntity != null) { + oldClosestEntity.getEvents().trigger("hideToolTip"); + } + + if (closestEntity != null) { + closestEntity.getEvents().trigger("showToolTip"); + } } private void updateSpeed() { @@ -124,8 +102,6 @@ private void updateSpeed() { * Triggers an interaction event. It holds the logic in how to interact with a given station */ void interact(String type) { - Entity closestEntity = sensor.getClosestInteractable(); - closestEntity.getEvents().trigger("Station Interaction", playerInventory, displayInventory, type); } From b90a1a293a725f3a5fc34b307aaabddd23a3af9b Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 22:24:03 +1000 Subject: [PATCH 10/14] Formatting fix --- .../game/components/player/PlayerActions.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java index 0fad3254a..22583b977 100644 --- a/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java +++ b/source/core/src/main/com/csse3200/game/components/player/PlayerActions.java @@ -38,28 +38,28 @@ public void create() { entity.getEvents().addListener("interact", this::interact); } - @Override - public void update() { - Body body = physicsComponent.getBody(); - Vector2 position = body.getPosition(); - - if (moving) { - updateInteraction(); - - // Stop if it's at min x position or max x position - if (position.x < MIN_X_POSITION) { - position.x = MIN_X_POSITION; - body.setTransform(MIN_X_POSITION, position.y, body.getAngle()); - stopWalking(); - } else if (position.x > MAX_X_POSITION) { - position.x = MAX_X_POSITION; - body.setTransform(MAX_X_POSITION, position.y, body.getAngle()); - stopWalking(); - } else { - updateSpeed(); + @Override + public void update() { + Body body = physicsComponent.getBody(); + Vector2 position = body.getPosition(); + + if (moving) { + updateInteraction(); + + // Stop if it's at min x position or max x position + if (position.x < MIN_X_POSITION) { + position.x = MIN_X_POSITION; + body.setTransform(MIN_X_POSITION, position.y, body.getAngle()); + stopWalking(); + } else if (position.x > MAX_X_POSITION) { + position.x = MAX_X_POSITION; + body.setTransform(MAX_X_POSITION, position.y, body.getAngle()); + stopWalking(); + } else { + updateSpeed(); + } } } - } /** * Updates the player's interaction with nearby objects. This method checks for the closest From d91cdb895babcca74c0489b1c7614badd1839744 Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 22:49:25 +1000 Subject: [PATCH 11/14] Added comments and fixed any potential code smells --- .../game/components/SensorComponent.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/SensorComponent.java b/source/core/src/main/com/csse3200/game/components/SensorComponent.java index 1280dbc39..23311f910 100644 --- a/source/core/src/main/com/csse3200/game/components/SensorComponent.java +++ b/source/core/src/main/com/csse3200/game/components/SensorComponent.java @@ -7,12 +7,16 @@ import java.util.Map; /** - * Finds the closest interactable object inside a range of the player + * Finds the closest interactable object inside a range of the player's position. + * This component is used everytime the player moves to update the nearby + * interactable entities. */ public class SensorComponent extends Component { - Vector2 playerPosition; - Map interactables; + private static final float INTERACTION_RANGE = 1.15f; + + private Vector2 playerPosition; + private Map interactables; @Override public void create() { @@ -20,6 +24,10 @@ public void create() { interactables = ServiceLocator.getInteractableService().getInteractables(); } + /** + * Updates the closest interactable object to the player. + * @return the closest interactable object to the player. + */ public Entity getClosestInteractable() { Entity closestEntity = null; @@ -31,7 +39,7 @@ public Entity getClosestInteractable() { float distance = playerPosition.dst(entityPosition); - if (distance <= 1.15f && distance < closestDistance) { + if (distance <= INTERACTION_RANGE && distance < closestDistance) { closestDistance = distance; closestEntity = entity; } From 1f27d53a87b5a0762b33a40289a29e36caf940d4 Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 23:04:02 +1000 Subject: [PATCH 12/14] Added hover display for collected stations specifically, fixed but where item state would not update upon stopping chopping / cooking and added tooltips display to all baskets / stations that needed it --- .../InventoryDisplayHoverComponent.java | 66 ++++++++++++------- .../entities/factories/StationFactory.java | 8 +++ 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java b/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java index bf5bd8c58..dbd016473 100644 --- a/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java +++ b/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java @@ -12,6 +12,10 @@ import com.csse3200.game.components.items.ItemComponent; import java.util.ArrayList; import java.util.Objects; + +import com.csse3200.game.components.station.IngredientStationHandlerComponent; +import com.csse3200.game.components.station.StationChoppingComponent; +import com.csse3200.game.components.station.StationCookingComponent; import com.csse3200.game.components.station.StationMealComponent; import com.csse3200.game.physics.components.PhysicsComponent; @@ -32,6 +36,9 @@ public class InventoryDisplayHoverComponent extends RenderComponent { private Texture selectedBackgroundImage; private boolean showKeys = false; private boolean isMixingStation = false; + private boolean isBasket = false; + private boolean isCooking = false; + private boolean isChopping = false; private Texture interactKeyImage; private Texture combineKeyImage; private Texture rotateKeyImage; @@ -64,6 +71,9 @@ public void create() { entity.getEvents().addListener("hideToolTip", this::hideToolTip); isMixingStation = entity.getComponent(StationMealComponent.class) != null; + isBasket = entity.getComponent(IngredientStationHandlerComponent.class) != null; + isCooking = entity.getComponent(StationCookingComponent.class) != null; + isChopping = entity.getComponent(StationChoppingComponent.class) != null; // need to use the physics body position of the entity as // the regular getPosition() on stations does not return the correct position. @@ -105,7 +115,9 @@ private void updateImages() { */ @Override public void update() { - + if (isCooking || isChopping) { + updateImages(); + } } public void updateDisplay() { @@ -130,6 +142,35 @@ private void hideToolTip() { public void draw(SpriteBatch batch) { if (entity == null || position == null || scale == null) return; + + if (showKeys) { + batch.draw(interactKeyImage, + position.x, + position.y + 0.7f, + KEY_WIDTH, + KEY_HEIGHT + ); + if (isMixingStation) { + batch.draw(rotateKeyImage, + position.x, + position.y + 0.4f, + KEY_WIDTH, + KEY_HEIGHT + ); + batch.draw(combineKeyImage, + position.x, + position.y + 0.1f, + KEY_WIDTH, + KEY_HEIGHT + ); + } + } + + // If we have a basked don't draw the images + if (isBasket) { + return; + } + for (int i = 0; i < itemImages.size(); i++) { // draw selected background image for the next item to be taken out // (if there is more than 1 item displayed) @@ -154,29 +195,6 @@ public void draw(SpriteBatch batch) { SLOT_WIDTH - 0.2f, SLOT_HEIGHT - 0.2f ); - } - if (showKeys) { - batch.draw(interactKeyImage, - position.x, - position.y + 0.7f, - KEY_WIDTH, - KEY_HEIGHT - ); - if (isMixingStation) { - batch.draw(rotateKeyImage, - position.x, - position.y + 0.4f, - KEY_WIDTH, - KEY_HEIGHT - ); - batch.draw(combineKeyImage, - position.x, - position.y + 0.1f, - KEY_WIDTH, - KEY_HEIGHT - ); - } - } } diff --git a/source/core/src/main/com/csse3200/game/entities/factories/StationFactory.java b/source/core/src/main/com/csse3200/game/entities/factories/StationFactory.java index 1ab55b287..b8c6dc9b3 100644 --- a/source/core/src/main/com/csse3200/game/entities/factories/StationFactory.java +++ b/source/core/src/main/com/csse3200/game/entities/factories/StationFactory.java @@ -177,6 +177,7 @@ public static Entity createBananaBasket() { .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE)) .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) + .addComponent(new InventoryDisplayHoverComponent()) .addComponent(new StationCollectionComponent()) .addComponent(new InventoryComponent(1)) .addComponent(new IngredientStationHandlerComponent("bananaTree", "banana")); @@ -205,6 +206,7 @@ public static Entity createStrawberryBasket() { .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE)) .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) + .addComponent(new InventoryDisplayHoverComponent()) .addComponent(new StationCollectionComponent()) .addComponent(new InventoryComponent(1)) .addComponent(new IngredientStationHandlerComponent("strawberriesStation", "strawberry")); @@ -232,6 +234,7 @@ public static Entity createAcaiBasket() { .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE)) .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) + .addComponent(new InventoryDisplayHoverComponent()) .addComponent(new StationCollectionComponent()) .addComponent(new InventoryComponent(1)) .addComponent(new IngredientStationHandlerComponent("acaiStation", "acai")); @@ -259,6 +262,7 @@ public static Entity createLettuceBasket() { .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE)) .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) + .addComponent(new InventoryDisplayHoverComponent()) .addComponent(new StationCollectionComponent()) .addComponent(new InventoryComponent(1)) .addComponent(new IngredientStationHandlerComponent("lettuceStation", "lettuce")); @@ -287,6 +291,7 @@ public static Entity createTomatoBasket() { .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) .addComponent(new StationCollectionComponent()) + .addComponent(new InventoryDisplayHoverComponent()) .addComponent(new InventoryComponent(1)) .addComponent(new IngredientStationHandlerComponent("tomatoStation", "tomato")); // Physics components @@ -315,6 +320,7 @@ public static Entity createCucumberBasket() { .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) .addComponent(new StationCollectionComponent()) + .addComponent(new InventoryDisplayHoverComponent()) .addComponent(new InventoryComponent(1)) .addComponent(new IngredientStationHandlerComponent("cucumberStation", "cucumber")); // Physics components @@ -341,6 +347,7 @@ public static Entity createBeefFridge() { .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE)) .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) + .addComponent(new InventoryDisplayHoverComponent()) .addComponent(new StationCollectionComponent()) .addComponent(new InventoryComponent(1)) .addComponent(new IngredientStationHandlerComponent("beefStation", "beef")); @@ -368,6 +375,7 @@ public static Entity createChocolateFridge() { .addComponent(new ColliderComponent().setLayer(PhysicsLayer.OBSTACLE)) .addComponent(new InteractionComponent(PhysicsLayer.INTERACTABLE)) .addComponent(new TooltipsDisplay()) + .addComponent(new InventoryDisplayHoverComponent()) .addComponent(new StationCollectionComponent()) .addComponent(new InventoryComponent(1)) .addComponent(new IngredientStationHandlerComponent("beefStation", "chocolate")); From 492a509cac3ab7e4bcb478669bb05f9130e16cd4 Mon Sep 17 00:00:00 2001 From: Callan Date: Fri, 11 Oct 2024 23:09:17 +1000 Subject: [PATCH 13/14] Further improved on bugfix for items not updating upon being cooked / chopped to use an event to trigger it --- .../player/InventoryDisplayHoverComponent.java | 10 +--------- .../components/station/StationProgressDisplay.java | 3 +-- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java b/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java index dbd016473..dd7870467 100644 --- a/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java +++ b/source/core/src/main/com/csse3200/game/components/player/InventoryDisplayHoverComponent.java @@ -14,8 +14,6 @@ import java.util.Objects; import com.csse3200.game.components.station.IngredientStationHandlerComponent; -import com.csse3200.game.components.station.StationChoppingComponent; -import com.csse3200.game.components.station.StationCookingComponent; import com.csse3200.game.components.station.StationMealComponent; import com.csse3200.game.physics.components.PhysicsComponent; @@ -37,8 +35,6 @@ public class InventoryDisplayHoverComponent extends RenderComponent { private boolean showKeys = false; private boolean isMixingStation = false; private boolean isBasket = false; - private boolean isCooking = false; - private boolean isChopping = false; private Texture interactKeyImage; private Texture combineKeyImage; private Texture rotateKeyImage; @@ -72,8 +68,6 @@ public void create() { isMixingStation = entity.getComponent(StationMealComponent.class) != null; isBasket = entity.getComponent(IngredientStationHandlerComponent.class) != null; - isCooking = entity.getComponent(StationCookingComponent.class) != null; - isChopping = entity.getComponent(StationChoppingComponent.class) != null; // need to use the physics body position of the entity as // the regular getPosition() on stations does not return the correct position. @@ -115,9 +109,7 @@ private void updateImages() { */ @Override public void update() { - if (isCooking || isChopping) { - updateImages(); - } + } public void updateDisplay() { diff --git a/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java b/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java index b719abdb3..8701f0467 100644 --- a/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java +++ b/source/core/src/main/com/csse3200/game/components/station/StationProgressDisplay.java @@ -9,7 +9,6 @@ import com.csse3200.game.rendering.RenderComponent; import com.csse3200.game.services.ServiceLocator; import com.csse3200.game.components.items.ItemComponent; -import com.csse3200.game.components.player.InventoryComponent; import com.csse3200.game.components.items.ItemTimerComponent; import com.csse3200.game.components.items.ChopIngredientComponent; import com.csse3200.game.components.items.CookIngredientComponent; @@ -55,7 +54,6 @@ public void create() { // the regular getPosition() on stations does not return the correct position. position = entity.getComponent(PhysicsComponent.class).getBody().getPosition(); scale = entity.getScale(); - InventoryComponent stationInventory = entity.getComponent(InventoryComponent.class); } } @@ -89,6 +87,7 @@ public void update() { displayBar = true; } else { displayBar = false; + entity.getEvents().trigger("updateInventory"); } } From 1dc973d021c620e7af1c80aff3f30563343f88cb Mon Sep 17 00:00:00 2001 From: Callan Date: Sat, 12 Oct 2024 01:31:34 +1000 Subject: [PATCH 14/14] Rewrote tests for sensor component' --- .../interaction/SensorComponentTest.java | 297 +++++------------- 1 file changed, 84 insertions(+), 213 deletions(-) diff --git a/source/core/src/test/com/csse3200/game/components/interaction/SensorComponentTest.java b/source/core/src/test/com/csse3200/game/components/interaction/SensorComponentTest.java index 75bd8eaee..a60fc7d8b 100644 --- a/source/core/src/test/com/csse3200/game/components/interaction/SensorComponentTest.java +++ b/source/core/src/test/com/csse3200/game/components/interaction/SensorComponentTest.java @@ -1,275 +1,146 @@ package com.csse3200.game.components.interaction; import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.physics.box2d.Fixture; +import com.badlogic.gdx.physics.box2d.Body; import com.csse3200.game.components.SensorComponent; import com.csse3200.game.entities.Entity; import com.csse3200.game.extensions.GameExtension; import com.csse3200.game.physics.PhysicsService; -import com.csse3200.game.physics.PhysicsLayer; import com.csse3200.game.physics.components.PhysicsComponent; -import com.csse3200.game.physics.components.InteractionComponent; +import com.csse3200.game.services.InteractableService; import com.csse3200.game.services.ServiceLocator; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @ExtendWith(GameExtension.class) -class SensorComponentTest { +public class SensorComponentTest { private SensorComponent sensorComponent; // Declare sensorComponent here - private final short interactableLayer = PhysicsLayer.INTERACTABLE; + private Entity mockPlayer; + private PhysicsComponent mockPhysics; + private Body mockBody; - /*/ @BeforeEach void beforeEach() { - ServiceLocator.registerPhysicsService(new PhysicsService()); + ServiceLocator.clear(); + + // Create the mock player with set position of (0, 0) sensorComponent = new SensorComponent(); assertNotNull(sensorComponent, "sensor component should have been initialised"); - } - @Test - void checkSensorInit() { - assertNotNull(this.sensorComponent, "The sensorComponent should not be null"); - // Set up entities - Entity entity = createEntity(0, 0); - Entity target = createTarget(0, 0); + ServiceLocator.registerPhysicsService(new PhysicsService()); - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture targetFixture = target.getComponent(InteractionComponent.class).getFixture(); - //Ensure the sensor component is set up correctly - InteractionComponent sensorInteractionComponent = this.sensorComponent.getInteractionComponent(); - assertNotNull(sensorInteractionComponent, "The interaction component for sensor should not be null"); - - Fixture sensorFixture = sensorInteractionComponent.getFixture(); - assertNotNull(sensorFixture, "The fixture for interaction component of sensor should not be null"); + mockBody = mock(Body.class); + mockPhysics = mock(PhysicsComponent.class); + when(mockPhysics.getBody()).thenReturn(mockBody); + when(mockBody.getPosition()).thenReturn(new Vector2(0, 0)); + mockPlayer = new Entity(); + mockPlayer.addComponent(mockPhysics); + mockPlayer.addComponent(sensorComponent); - //Ensure the entity interaction component matchest the sensor interaction component - assertSame(sensorFixture, entityFixture, "Sensore fixture should match entity fixture"); - } + // Create an interactable service to be able to register entities + ServiceLocator.registerInteractableService(new InteractableService()); - @Test - void checkEntityInit() { - Entity entity = createEntity(0, 0); - InteractionComponent entityInteractionComponent = entity.getComponent(InteractionComponent.class); - assertEquals(PhysicsLayer.INTERACTABLE, entityInteractionComponent.getLayer()); - Fixture entityFixture = entityInteractionComponent.getFixture(); - assertNotNull(entityFixture, "The fixture for InteractionComponent should not be null"); - assertEquals(interactableLayer, entityFixture.getFilterData().categoryBits); - assertTrue(PhysicsLayer.contains(interactableLayer, entityFixture.getFilterData().categoryBits), - "entity fixture should be in the physics layer"); + // Create the player entity + mockPlayer.create(); } - @Test - void checkTargetInit() { - Entity target = createTarget(0, 0); - InteractionComponent targetInteractionComponent = target.getComponent(InteractionComponent.class); - assertEquals(PhysicsLayer.INTERACTABLE, targetInteractionComponent.getLayer()); + private Entity createEntityAt(float x, float y) { + Body mockBodyOther = mock(Body.class); + PhysicsComponent mockPhysicsOther = mock(PhysicsComponent.class); + when(mockPhysicsOther.getBody()).thenReturn(mockBodyOther); + when(mockBodyOther.getPosition()).thenReturn(new Vector2(x, y)); + + Entity mockEntity = new Entity(); + mockEntity.addComponent(mockPhysicsOther); - Fixture targetFixture = targetInteractionComponent.getFixture(); - assertNotNull(targetFixture, "The fixture for InteractionComponent should not be null"); - assertEquals(targetFixture.getFilterData().categoryBits, interactableLayer); - assertTrue(PhysicsLayer.contains(interactableLayer, targetFixture.getFilterData().categoryBits), - "entity fixture should be in the physics layer"); + return mockEntity; } @Test - void shouldAddFixtureToList() { - Entity entity = createEntity(0, 0); - Entity target = createTarget(0, 0); - - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture targetFixture = target.getComponent(InteractionComponent.class).getFixture(); - - assertEquals(targetFixture.getFilterData().categoryBits, entityFixture.getFilterData().categoryBits, - "Target fixture layer should match entity fixture layer"); - - assertNotEquals(sensorComponent.getInteractionComponent().getFixture(), targetFixture); - assertTrue(PhysicsLayer.contains(interactableLayer, targetFixture.getFilterData().categoryBits), - "target fixture should be in the physics layer"); - assertTrue(sensorComponent.isWithinDistance(targetFixture, 1)); - - sensorComponent.onCollisionStart(entityFixture, targetFixture); - assertEquals(1, sensorComponent.getNumFixtures(), - "There should be one fixture added to the list"); + void TestSensorInit() { + Vector2 playerPosition = mockPlayer.getComponent(PhysicsComponent.class).getBody().getPosition(); + assertTrue(playerPosition.isZero()); } - @Test - void shouldDetectClosestFixture() { - Entity entity = createEntity(0, 0); - Entity target = createTarget(0, 0); - - InteractionComponent targetInteractionComponent = target.getComponent(InteractionComponent.class); - Fixture targetFixture = targetInteractionComponent.getFixture(); - - InteractionComponent entityInteractionComponent = entity.getComponent(InteractionComponent.class); - Fixture entityFixture = entityInteractionComponent.getFixture(); - - sensorComponent.onCollisionStart(entityFixture, targetFixture); - - Fixture closestFixture = sensorComponent.getClosestFixture(); - assertNotNull(closestFixture, "Closest fixture should not be null"); - assertEquals(targetFixture, closestFixture, "The closest fixture should be the target's fixture"); - } - - @Test - void shouldNotDetectFarFeature() { - Entity entity = createEntity(0, 0); - Entity target = createTarget(10, 10); - - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture targetFixture = target.getComponent(InteractionComponent.class).getFixture(); - - assertEquals(targetFixture.getFilterData().categoryBits, entityFixture.getFilterData().categoryBits, - "Target fixture layer should match entity fixture layer"); - - assertNotEquals(sensorComponent.getInteractionComponent().getFixture(), targetFixture); - assertTrue(PhysicsLayer.contains(interactableLayer, targetFixture.getFilterData().categoryBits), - "target fixture should be in the physics layer"); - assertFalse(sensorComponent.isWithinDistance(targetFixture, 1)); - - sensorComponent.onCollisionStart(entityFixture, targetFixture); - assertEquals(0, sensorComponent.getNumFixtures(), - "There should be no fixture added to the list"); - Fixture closestFixture = sensorComponent.getClosestFixture(); - assertNull(closestFixture, "Closest fixture should be null"); - } - - @Test - void shouldNotDetectBadTarget() { - Entity entity = createEntity(0, 0); - Entity badTarget = createBadTarget(0, 0); - - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture badTargetFixture = badTarget.getComponent(InteractionComponent.class).getFixture(); - - sensorComponent.onCollisionStart(entityFixture, badTargetFixture); - - // Should not detect a target with the wrong layer - assertEquals(0, sensorComponent.getNumFixtures(), - "There should be no fixture added to the list"); - Fixture closestFixture = sensorComponent.getClosestFixture(); - assertNull(closestFixture, "Closest fixture should be null"); - } - - @Test - void shouldRemoveTargetAfterCollisionEnded() { - Entity entity = createEntity(0, 0); - Entity target = createTarget(0, 0); - - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture targetFixture = target.getComponent(InteractionComponent.class).getFixture(); - - sensorComponent.onCollisionStart(entityFixture, targetFixture); - - Fixture closestFixture = sensorComponent.getClosestFixture(); - assertNotNull(closestFixture, "Closest fixture should not be null"); - assertEquals(targetFixture, closestFixture, "The closest fixture should be the target's fixture"); - - // Move the entity out of collision box - entity.setPosition(20, 20); - sensorComponent.onCollisionEnd(entityFixture, targetFixture); - - closestFixture = sensorComponent.getClosestFixture(); - assertNull(closestFixture, "Closest fixture should now be null"); - } - - private Entity createEntity(float x, float y) { - Entity entity = new Entity(); - entity.setPosition(x, y); - entity.addComponent(new PhysicsComponent()); // Initialize and add PhysicsComponent - entity.addComponent(sensorComponent); // Add SensorComponent - InteractionComponent component = new InteractionComponent(PhysicsLayer.INTERACTABLE); - entity.addComponent(component); - entity.create(); // Ensure components are created - return entity; + @Test + void TestSensorNoInteractables() { + Entity closestEntity = mockPlayer.getComponent(SensorComponent.class).getClosestInteractable(); + assertNull(closestEntity); } - private Entity createTarget(float x, float y) { - Entity target = new Entity(); - target.setPosition(x, y); - InteractionComponent interactionComponent = new InteractionComponent(PhysicsLayer.INTERACTABLE); - target.addComponent(new PhysicsComponent()); - target.addComponent(interactionComponent); - target.create(); - return target; - } + @Test + void TestSensorOneInteractableOutOfRange() { + Entity mockEntity = createEntityAt(1, 1); + ServiceLocator.getInteractableService().registerEntity(mockEntity); - private Entity createBadTarget(float x, float y) { - Entity target = new Entity(); - target.setPosition(x, y); - InteractionComponent interactionComponent = new InteractionComponent(PhysicsLayer.NPC); - target.addComponent(new PhysicsComponent()); - target.addComponent(interactionComponent); - target.create(); - return target; + Entity closestEntity = sensorComponent.getClosestInteractable(); + assertNull(closestEntity); } @Test - void detectMultipleFixtures() { - Entity entity = createEntity(0, 0); - Entity target1 = createTarget(0.5f, 0.5f); - Entity target2 = createTarget(0.7f, 0.7f); - - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture target1Fixture = target1.getComponent(InteractionComponent.class).getFixture(); - Fixture target2Fixture = target2.getComponent(InteractionComponent.class).getFixture(); + void TestSensorOneInteractableInRange() { + Entity mockEntity = createEntityAt(0, 1); + ServiceLocator.getInteractableService().registerEntity(mockEntity); - sensorComponent.onCollisionStart(entityFixture, target1Fixture); - sensorComponent.onCollisionStart(entityFixture, target2Fixture); - - assertEquals(2, sensorComponent.getNumFixtures(), "Two fixtures should be detected"); + Entity closestEntity = sensorComponent.getClosestInteractable(); + assertNotNull(closestEntity); + assertEquals(mockEntity, closestEntity); } @Test - void removeAllTargetsAfterCollisionsEnded() { - Entity entity = createEntity(0, 0); - Entity target1 = createTarget(0.5f, 0.5f); - Entity target2 = createTarget(0.7f, 0.7f); + void TestSensorTwoInteractableBothOutOfRange() { + Entity mockEntity1 = createEntityAt(2, 1); + ServiceLocator.getInteractableService().registerEntity(mockEntity1); - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture target1Fixture = target1.getComponent(InteractionComponent.class).getFixture(); - Fixture target2Fixture = target2.getComponent(InteractionComponent.class).getFixture(); + Entity mockEntity2 = createEntityAt(1, 1); + ServiceLocator.getInteractableService().registerEntity(mockEntity2); - sensorComponent.onCollisionStart(entityFixture, target1Fixture); - sensorComponent.onCollisionStart(entityFixture, target2Fixture); + Entity closestEntity = sensorComponent.getClosestInteractable(); + assertNull(closestEntity); + } - assertEquals(2, sensorComponent.getNumFixtures(), "There should be two fixtures detected"); + @Test + void TestSensorTwoInteractableBothInRangeBigToSmall() { + Entity mockEntity1 = createEntityAt(0f, 1f); + ServiceLocator.getInteractableService().registerEntity(mockEntity1); - sensorComponent.onCollisionEnd(entityFixture, target1Fixture); - sensorComponent.onCollisionEnd(entityFixture, target2Fixture); + Entity mockEntity2 = createEntityAt(0.5f, 0f); + ServiceLocator.getInteractableService().registerEntity(mockEntity2); - assertEquals(0, sensorComponent.getNumFixtures(), "All fixtures should be removed after collision ends"); + Entity closestEntity = sensorComponent.getClosestInteractable(); + assertNotNull(closestEntity); + assertEquals(mockEntity2, closestEntity); } @Test - void ignoreTargetOutsideLayer() { - Entity entity = createEntity(0, 0); - Entity badTarget = createBadTarget(0.5f, 0.5f); + void TestSensorTwoInteractableBothInRangeSmallToBig() { + Entity mockEntity1 = createEntityAt(0.5f, 0f); + ServiceLocator.getInteractableService().registerEntity(mockEntity1); - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture badTargetFixture = badTarget.getComponent(InteractionComponent.class).getFixture(); + Entity mockEntity2 = createEntityAt(0.5f, 1f); + ServiceLocator.getInteractableService().registerEntity(mockEntity2); - sensorComponent.onCollisionStart(entityFixture, badTargetFixture); - - assertEquals(0, sensorComponent.getNumFixtures(), "No fixture should be added for target outside the interaction layer"); + Entity closestEntity = sensorComponent.getClosestInteractable(); + assertNotNull(closestEntity); + assertEquals(mockEntity1, closestEntity); } @Test - void shouldIgnoreRemovedFixtureAfterCollisionEnd() { - Entity entity = createEntity(0, 0); - Entity target = createTarget(0.5f, 0.5f); - - Fixture entityFixture = entity.getComponent(InteractionComponent.class).getFixture(); - Fixture targetFixture = target.getComponent(InteractionComponent.class).getFixture(); + void TestSensorTwoInteractableOneInRange() { + Entity mockEntity1 = createEntityAt(1, 1); + ServiceLocator.getInteractableService().registerEntity(mockEntity1); - sensorComponent.onCollisionStart(entityFixture, targetFixture); - sensorComponent.onCollisionEnd(entityFixture, targetFixture); + Entity mockEntity2 = createEntityAt(0.5f, 0f); + ServiceLocator.getInteractableService().registerEntity(mockEntity2); - // Ensure the target is removed from the sensor's list - assertEquals(0, sensorComponent.getNumFixtures(), "The fixture should be removed after the collision ends"); + Entity closestEntity = sensorComponent.getClosestInteractable(); + assertNotNull(closestEntity); + assertEquals(mockEntity2, closestEntity); } - */ }