From a9be5fa675285d120f47097d404b0d202b142c12 Mon Sep 17 00:00:00 2001 From: jzitnik-dev Date: Fri, 2 Jan 2026 01:32:39 +0100 Subject: [PATCH] feat: Inventory item mouse hover animation --- .../events/handlers/FullRoomDrawHandler.java | 9 +- .../handlers/InventoryRerenderHandler.java | 93 +----------- .../handlers/MouseActionEventHandler.java | 8 +- .../java/cz/jzitnik/game/objects/Chest.java | 5 +- .../game/objects/GlobalUIClickHandler.java | 11 ++ .../jzitnik/game/objects/UIClickHandler.java | 2 + src/main/java/cz/jzitnik/ui/Inventory.java | 143 ++++++++++++++++++ .../java/cz/jzitnik/ui/{ => utils}/Grid.java | 43 ++++-- .../utils/UIClickHandlerRepository.java | 61 ++++++-- 9 files changed, 257 insertions(+), 118 deletions(-) create mode 100644 src/main/java/cz/jzitnik/game/objects/GlobalUIClickHandler.java create mode 100644 src/main/java/cz/jzitnik/ui/Inventory.java rename src/main/java/cz/jzitnik/ui/{ => utils}/Grid.java (77%) diff --git a/src/main/java/cz/jzitnik/events/handlers/FullRoomDrawHandler.java b/src/main/java/cz/jzitnik/events/handlers/FullRoomDrawHandler.java index d1b3e4d..20408db 100644 --- a/src/main/java/cz/jzitnik/events/handlers/FullRoomDrawHandler.java +++ b/src/main/java/cz/jzitnik/events/handlers/FullRoomDrawHandler.java @@ -18,8 +18,10 @@ import cz.jzitnik.game.ResourceManager; import cz.jzitnik.states.RenderState; import cz.jzitnik.states.ScreenBuffer; import cz.jzitnik.states.TerminalState; +import cz.jzitnik.ui.Inventory; import cz.jzitnik.utils.DependencyManager; import cz.jzitnik.utils.RerenderUtils; +import cz.jzitnik.utils.UIClickHandlerRepository; import cz.jzitnik.utils.events.AbstractEventHandler; import cz.jzitnik.utils.events.EventManager; import cz.jzitnik.utils.roomtasks.RoomTaskScheduler; @@ -52,6 +54,10 @@ public class FullRoomDrawHandler extends AbstractEventHandler { private Debugging debugging; @InjectDependency private RoomTaskScheduler roomTaskScheduler; + @InjectDependency + private UIClickHandlerRepository uiClickHandlerRepository; + @InjectDependency + private Inventory inventory; public FullRoomDrawHandler(DependencyManager dm) { super(dm); @@ -79,7 +85,8 @@ public class FullRoomDrawHandler extends AbstractEventHandler { RerenderUtils.rerenderPart(0, width - 1, 0, height - 1, startX, startY, currentRoom, room, player, playerTexture, screenBuffer, resourceManager, debugging); if (event.isFullRerender()) { - InventoryRerenderHandler.renderInventoryRerender(dm); + inventory.renderInventoryRerender(); + uiClickHandlerRepository.registerGlobalHandler(inventory); } partsToRerender.add(new RerenderScreen.ScreenPart( diff --git a/src/main/java/cz/jzitnik/events/handlers/InventoryRerenderHandler.java b/src/main/java/cz/jzitnik/events/handlers/InventoryRerenderHandler.java index 9a5f284..0a97bcc 100644 --- a/src/main/java/cz/jzitnik/events/handlers/InventoryRerenderHandler.java +++ b/src/main/java/cz/jzitnik/events/handlers/InventoryRerenderHandler.java @@ -1,114 +1,33 @@ package cz.jzitnik.events.handlers; import com.googlecode.lanterna.TerminalPosition; -import com.googlecode.lanterna.TerminalSize; -import com.googlecode.lanterna.TextColor; import cz.jzitnik.annotations.EventHandler; import cz.jzitnik.annotations.injectors.InjectDependency; import cz.jzitnik.events.InventoryRerender; import cz.jzitnik.events.RerenderScreen; -import cz.jzitnik.game.GameRoom; -import cz.jzitnik.game.GameState; -import cz.jzitnik.game.ResourceManager; -import cz.jzitnik.game.items.GameItem; -import cz.jzitnik.states.ScreenBuffer; -import cz.jzitnik.states.TerminalState; -import cz.jzitnik.ui.Grid; -import cz.jzitnik.ui.pixels.ColoredPixel; -import cz.jzitnik.ui.pixels.Pixel; +import cz.jzitnik.ui.Inventory; import cz.jzitnik.utils.DependencyManager; -import cz.jzitnik.utils.RerenderUtils; -import cz.jzitnik.utils.StateManager; import cz.jzitnik.utils.events.AbstractEventHandler; import cz.jzitnik.utils.events.EventManager; -import java.awt.image.BufferedImage; - @EventHandler(InventoryRerender.class) public class InventoryRerenderHandler extends AbstractEventHandler { @InjectDependency private EventManager eventManager; - private static final int ITEMS_X = 3; - private static final int ITEMS_Y = 5; - private static final int OUTER_BORDER_WIDTH = 3; - private static final int INNER_BORDER_WIDTH = 2; - private static final int ITEM_SIZE = 16; // Characters - private static final int ITEM_PADDING = 2; // padding on each side - - private static final Pixel BORDER_COLOR = - new ColoredPixel(new TextColor.RGB(255, 255, 255)); - private static final Pixel BACKGROUND_COLOR = - new ColoredPixel(new TextColor.RGB(166, 166, 166)); - - private static final int ITEM_SLOT_SIZE = ITEM_SIZE + ITEM_PADDING * 2; - private static final int INVENTORY_WIDTH = - OUTER_BORDER_WIDTH * 2 + - (ITEMS_X * (ITEM_SLOT_SIZE + INNER_BORDER_WIDTH) - INNER_BORDER_WIDTH); - private static final int INVENTORY_HEIGHT = - OUTER_BORDER_WIDTH * 2 + - (ITEMS_Y * (ITEM_SLOT_SIZE + INNER_BORDER_WIDTH) - INNER_BORDER_WIDTH); - private static final int REAL_INVENTORY_HEIGHT = - Math.ceilDiv(INVENTORY_HEIGHT, 2); // 2 pixels per terminal row + @InjectDependency + private Inventory inventory; public InventoryRerenderHandler(DependencyManager dm) { super(dm); } - public record RenderResponse(int offsetX, int offsetY) {} - - public static RenderResponse renderInventoryRerender(DependencyManager dm) { - StateManager stateManager = dm.getDependencyOrThrow(StateManager.class); - ResourceManager resourceManager = dm.getDependencyOrThrow(ResourceManager.class); - - GameState gameState = stateManager.getOrThrow(GameState.class); - GameRoom currentRoom = gameState.getCurrentRoom(); - BufferedImage room = resourceManager.getResource(currentRoom.getTexture()); - - TerminalState terminalState = stateManager.getOrThrow(TerminalState.class); - TerminalSize terminalSize = terminalState.getTerminalScreen().getTerminalSize(); - - var inventory = gameState.getPlayer().getInventory(); - var buffer = stateManager.getOrThrow(ScreenBuffer.class).getRenderedBuffer(); - - var roomStart = RerenderUtils.getStart(room, terminalSize); - int maxX = roomStart.getX() - 1; - - final int OFFSET_Y = - (((terminalSize.getRows() - 1) / 2) - (REAL_INVENTORY_HEIGHT / 2)) * 2; - final int OFFSET_X = - (maxX / 2) - (INVENTORY_WIDTH / 2); - - BufferedImage[] textures = inventory.stream() - .map(GameItem::getTexture) - .toArray(BufferedImage[]::new); - - Grid grid = new Grid( - ITEMS_X, - ITEMS_Y, - OUTER_BORDER_WIDTH, - INNER_BORDER_WIDTH, - ITEM_SIZE, - ITEM_PADDING, - BORDER_COLOR, - BACKGROUND_COLOR - ); - - Pixel[][] internalBuffer = grid.render(textures); - - for (int y = 0; y < grid.getHeight(); y++) { - System.arraycopy(internalBuffer[y], 0, buffer[y + OFFSET_Y], OFFSET_X, grid.getWidth()); - } - - return new RenderResponse(OFFSET_X, OFFSET_Y); - } - @Override public void handle(InventoryRerender event) { - RenderResponse renderResponse = renderInventoryRerender(dm); + inventory.renderInventoryRerender(); eventManager.emitEvent(new RerenderScreen(new RerenderScreen.ScreenPart( - new TerminalPosition(renderResponse.offsetX, renderResponse.offsetY), - new TerminalPosition(renderResponse.offsetX + INVENTORY_WIDTH, renderResponse.offsetY + INVENTORY_HEIGHT) + new TerminalPosition(inventory.getOffsetX(), inventory.getOffsetY()), + new TerminalPosition(inventory.getOffsetX() + Inventory.INVENTORY_WIDTH, inventory.getOffsetY() + Inventory.INVENTORY_HEIGHT) ))); } } diff --git a/src/main/java/cz/jzitnik/events/handlers/MouseActionEventHandler.java b/src/main/java/cz/jzitnik/events/handlers/MouseActionEventHandler.java index 2087437..142a619 100644 --- a/src/main/java/cz/jzitnik/events/handlers/MouseActionEventHandler.java +++ b/src/main/java/cz/jzitnik/events/handlers/MouseActionEventHandler.java @@ -53,7 +53,13 @@ public class MouseActionEventHandler extends AbstractEventHandler { } switch (event.getActionType()) { - case MOVE -> eventManager.emitEvent(new MouseMoveEvent(event)); + case MOVE -> { + boolean registered = uiClickHandlerRepository.handleMove(event); + + if (!registered) { + eventManager.emitEvent(new MouseMoveEvent(event)); + } + } case CLICK_RELEASE -> { boolean clicked = uiClickHandlerRepository.handleClick(event); diff --git a/src/main/java/cz/jzitnik/game/objects/Chest.java b/src/main/java/cz/jzitnik/game/objects/Chest.java index 5825b14..b79ba08 100644 --- a/src/main/java/cz/jzitnik/game/objects/Chest.java +++ b/src/main/java/cz/jzitnik/game/objects/Chest.java @@ -18,7 +18,7 @@ import cz.jzitnik.game.items.GameItem; import cz.jzitnik.game.utils.RoomCords; import cz.jzitnik.states.ScreenBuffer; import cz.jzitnik.states.TerminalState; -import cz.jzitnik.ui.Grid; +import cz.jzitnik.ui.utils.Grid; import cz.jzitnik.ui.pixels.ColoredPixel; import cz.jzitnik.ui.pixels.Empty; import cz.jzitnik.ui.pixels.Pixel; @@ -92,6 +92,7 @@ public final class Chest extends GameObject implements UIClickHandler { 16, // Item Size 1, // Item Padding (Changed to 1 as per your request example) new ColoredPixel(BORDER_COLOR), + new ColoredPixel(TRANSPARENT_COLOR), new ColoredPixel(TRANSPARENT_COLOR) ); } @@ -227,7 +228,7 @@ public final class Chest extends GameObject implements UIClickHandler { .map(GameItem::getTexture) .toArray(BufferedImage[]::new); - Pixel[][] uiPixels = grid.render(textures); + Pixel[][] uiPixels = grid.render(textures, -1); for (int y = 0; y < grid.getHeight(); y++) { for (int x = 0; x < grid.getWidth(); x++) { diff --git a/src/main/java/cz/jzitnik/game/objects/GlobalUIClickHandler.java b/src/main/java/cz/jzitnik/game/objects/GlobalUIClickHandler.java new file mode 100644 index 0000000..ccb2284 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/objects/GlobalUIClickHandler.java @@ -0,0 +1,11 @@ +package cz.jzitnik.game.objects; + +import cz.jzitnik.events.MouseAction; + +public interface GlobalUIClickHandler { + boolean handleClick(MouseAction mouseAction); + + default boolean handleMove(MouseAction ignoredMouseAction) { + return false; + } +} diff --git a/src/main/java/cz/jzitnik/game/objects/UIClickHandler.java b/src/main/java/cz/jzitnik/game/objects/UIClickHandler.java index fb617f8..f89c30d 100644 --- a/src/main/java/cz/jzitnik/game/objects/UIClickHandler.java +++ b/src/main/java/cz/jzitnik/game/objects/UIClickHandler.java @@ -4,4 +4,6 @@ import cz.jzitnik.events.MouseAction; public interface UIClickHandler { void handleClick(MouseAction mouseAction); + + default void handleMove(MouseAction ignoredMouseAction) {} } diff --git a/src/main/java/cz/jzitnik/ui/Inventory.java b/src/main/java/cz/jzitnik/ui/Inventory.java new file mode 100644 index 0000000..6bad8af --- /dev/null +++ b/src/main/java/cz/jzitnik/ui/Inventory.java @@ -0,0 +1,143 @@ +package cz.jzitnik.ui; + +import com.googlecode.lanterna.TerminalPosition; +import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.TextColor; +import cz.jzitnik.annotations.Dependency; +import cz.jzitnik.annotations.injectors.InjectDependency; +import cz.jzitnik.annotations.injectors.InjectState; +import cz.jzitnik.events.InventoryRerender; +import cz.jzitnik.events.MouseAction; +import cz.jzitnik.game.GameState; +import cz.jzitnik.game.ResourceManager; +import cz.jzitnik.game.items.GameItem; +import cz.jzitnik.game.objects.GlobalUIClickHandler; +import cz.jzitnik.states.ScreenBuffer; +import cz.jzitnik.states.TerminalState; +import cz.jzitnik.ui.pixels.ColoredPixel; +import cz.jzitnik.ui.pixels.Pixel; +import cz.jzitnik.ui.utils.Grid; +import cz.jzitnik.utils.RerenderUtils; +import cz.jzitnik.utils.events.EventManager; +import lombok.Getter; + +import java.awt.image.BufferedImage; + +@Dependency +public class Inventory implements GlobalUIClickHandler { + private static final int ITEMS_X = 3; + private static final int ITEMS_Y = 5; + private static final int OUTER_BORDER_WIDTH = 2; + private static final int INNER_BORDER_WIDTH = 1; + private static final int ITEM_SIZE = 16; // Characters + private static final int ITEM_PADDING = 2; // padding on each side + private static final Pixel BORDER_COLOR = + new ColoredPixel(new TextColor.RGB(255, 255, 255)); + private static final Pixel BACKGROUND_COLOR = + new ColoredPixel(new TextColor.RGB(166, 166, 166)); + private static final Pixel BACKGROUND_COLOR_SELECTED = + new ColoredPixel(new TextColor.RGB(186, 186, 186)); + private static final int ITEM_SLOT_SIZE = ITEM_SIZE + ITEM_PADDING * 2; + public static final int INVENTORY_WIDTH = + OUTER_BORDER_WIDTH * 2 + + (ITEMS_X * (ITEM_SLOT_SIZE + INNER_BORDER_WIDTH) - INNER_BORDER_WIDTH); + public static final int INVENTORY_HEIGHT = + OUTER_BORDER_WIDTH * 2 + + (ITEMS_Y * (ITEM_SLOT_SIZE + INNER_BORDER_WIDTH) - INNER_BORDER_WIDTH); + private static final int REAL_INVENTORY_HEIGHT = + Math.ceilDiv(INVENTORY_HEIGHT, 2); // 2 pixels per terminal row + + private static final Grid grid = new Grid( + ITEMS_X, + ITEMS_Y, + OUTER_BORDER_WIDTH, + INNER_BORDER_WIDTH, + ITEM_SIZE, + ITEM_PADDING, + BORDER_COLOR, + BACKGROUND_COLOR, + BACKGROUND_COLOR_SELECTED + ); + int selectedIndex = -1; + @InjectDependency + private ResourceManager resourceManager; + @InjectState + private GameState gameState; + @InjectState + private TerminalState terminalState; + @InjectState + private ScreenBuffer screenBuffer; + @InjectDependency + private EventManager eventManager; + @Getter + private int offsetX; + @Getter + private int offsetY; + + private void calculateOffset() { + BufferedImage room = resourceManager.getResource(ResourceManager.Resource.ROOM1); + TerminalSize terminalSize = terminalState.getTerminalScreen().getTerminalSize(); + var roomStart = RerenderUtils.getStart(room, terminalSize); + int maxX = roomStart.getX() - 1; + + offsetY = (((terminalSize.getRows() - 1) / 2) - (REAL_INVENTORY_HEIGHT / 2)) * 2; + offsetX = (maxX / 2) - (INVENTORY_WIDTH / 2); + } + + @Override + public boolean handleClick(MouseAction mouseAction) { + TerminalPosition terminalPosition = calculateActualCords(mouseAction.getPosition()); + + if (terminalPosition.getColumn() < 0 || terminalPosition.getRow() < 0 || terminalPosition.getRow() >= INVENTORY_HEIGHT || terminalPosition.getColumn() >= INVENTORY_WIDTH) { + return false; + } + + int itemClickedOnIndex = grid.getItemIndexAt(terminalPosition.getColumn(), terminalPosition.getRow()); + + // TODO: Clicking on items + + return true; + } + + @Override + public boolean handleMove(MouseAction mouseAction) { + TerminalPosition terminalPosition = calculateActualCords(mouseAction.getPosition()); + + if (terminalPosition.getColumn() < 0 || terminalPosition.getRow() < 0 || terminalPosition.getRow() >= INVENTORY_HEIGHT || terminalPosition.getColumn() >= INVENTORY_WIDTH) { + if (selectedIndex != -1) { + selectedIndex = -1; + eventManager.emitEvent(new InventoryRerender()); + } + return false; + } + + int newSelectedIndex = grid.getItemIndexAt(terminalPosition.getColumn(), terminalPosition.getRow()); + + if (newSelectedIndex != selectedIndex) { + selectedIndex = newSelectedIndex; + eventManager.emitEvent(new InventoryRerender()); + } + + return true; + } + + private TerminalPosition calculateActualCords(TerminalPosition position) { + return new TerminalPosition(position.getColumn() - offsetX, position.getRow() * 2 - offsetY); + } + + public void renderInventoryRerender() { + calculateOffset(); + var inventory = gameState.getPlayer().getInventory(); + var buffer = screenBuffer.getRenderedBuffer(); + + BufferedImage[] textures = inventory.stream() + .map(GameItem::getTexture) + .toArray(BufferedImage[]::new); + + Pixel[][] internalBuffer = grid.render(textures, selectedIndex); + + for (int y = 0; y < grid.getHeight(); y++) { + System.arraycopy(internalBuffer[y], 0, buffer[y + offsetY], offsetX, grid.getWidth()); + } + } +} diff --git a/src/main/java/cz/jzitnik/ui/Grid.java b/src/main/java/cz/jzitnik/ui/utils/Grid.java similarity index 77% rename from src/main/java/cz/jzitnik/ui/Grid.java rename to src/main/java/cz/jzitnik/ui/utils/Grid.java index b916cff..d9c5a96 100644 --- a/src/main/java/cz/jzitnik/ui/Grid.java +++ b/src/main/java/cz/jzitnik/ui/utils/Grid.java @@ -1,4 +1,4 @@ -package cz.jzitnik.ui; +package cz.jzitnik.ui.utils; import cz.jzitnik.ui.pixels.ColoredPixel; import cz.jzitnik.ui.pixels.Pixel; @@ -14,6 +14,7 @@ public class Grid { private final Pixel borderColor; private final Pixel backgroundColor; + private final Pixel backgroundColorSelected; private final int itemSlotSize; private final int cellSize; @@ -28,7 +29,8 @@ public class Grid { int itemSize, int itemPadding, Pixel borderColor, - Pixel backgroundColor + Pixel backgroundColor, + Pixel backgroundColorSelected ) { this.itemsX = itemsX; this.itemsY = itemsY; @@ -37,6 +39,7 @@ public class Grid { this.itemPadding = itemPadding; this.borderColor = borderColor; this.backgroundColor = backgroundColor; + this.backgroundColorSelected = backgroundColorSelected; this.itemSlotSize = itemSize + itemPadding * 2; this.cellSize = itemSlotSize + innerBorderWidth; @@ -50,10 +53,9 @@ public class Grid { (itemsY * (itemSlotSize + innerBorderWidth) - innerBorderWidth); } - public Pixel[][] render(BufferedImage[] textures) { + public Pixel[][] render(BufferedImage[] textures, int selectedIndex) { Pixel[][] buffer = new Pixel[gridHeight][gridWidth]; - // Background + borders for (int y = 0; y < gridHeight; y++) { for (int x = 0; x < gridWidth; x++) { @@ -77,7 +79,6 @@ public class Grid { } } - // Items int maxItems = Math.min(textures.length, itemsX * itemsY); for (int index = 0; index < maxItems; index++) { @@ -87,29 +88,39 @@ public class Grid { int itemX = index % itemsX; int itemY = index / itemsX; - int baseX = - outerBorderWidth + - itemX * cellSize + - itemPadding; + int slotX = outerBorderWidth + itemX * cellSize; + int slotY = outerBorderWidth + itemY * cellSize; - int baseY = - outerBorderWidth + - itemY * cellSize + - itemPadding; + if (index == selectedIndex) { + for (int dy = 0; dy < itemSlotSize; dy++) { + for (int dx = 0; dx < itemSlotSize; dx++) { + if (slotY + dy < gridHeight && slotX + dx < gridWidth) { + buffer[slotY + dy][slotX + dx] = backgroundColorSelected; + } + } + } + } + + int textureBaseX = slotX + itemPadding; + int textureBaseY = slotY + itemPadding; for (int y = 0; y < texture.getHeight(); y++) { for (int x = 0; x < texture.getWidth(); x++) { int pixel = texture.getRGB(x, y); int alpha = (pixel >> 24) & 0xff; - if (alpha == 0) continue; + if (alpha == 0) { + continue; + } int r = (pixel >> 16) & 0xff; int g = (pixel >> 8) & 0xff; int b = pixel & 0xff; - buffer[baseY + y][baseX + x] = - new ColoredPixel(new com.googlecode.lanterna.TextColor.RGB(r, g, b)); + if (textureBaseY + y < gridHeight && textureBaseX + x < gridWidth) { + buffer[textureBaseY + y][textureBaseX + x] = + new ColoredPixel(new com.googlecode.lanterna.TextColor.RGB(r, g, b)); + } } } } diff --git a/src/main/java/cz/jzitnik/utils/UIClickHandlerRepository.java b/src/main/java/cz/jzitnik/utils/UIClickHandlerRepository.java index 20451d5..7b1bd98 100644 --- a/src/main/java/cz/jzitnik/utils/UIClickHandlerRepository.java +++ b/src/main/java/cz/jzitnik/utils/UIClickHandlerRepository.java @@ -7,17 +7,28 @@ import cz.jzitnik.events.MouseAction; import cz.jzitnik.events.RerenderScreen; import cz.jzitnik.game.GameRoom; import cz.jzitnik.game.GameState; +import cz.jzitnik.game.objects.GlobalUIClickHandler; import cz.jzitnik.game.objects.UIClickHandler; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @Dependency public class UIClickHandlerRepository { + private final Map> roomSpecificHandlers = new ConcurrentHashMap<>(); + private final Set globalHandlers = ConcurrentHashMap.newKeySet(); @InjectState private GameState gameState; - private final Map> roomSpecificHandlers = new ConcurrentHashMap<>(); + public int registerGlobalHandler(GlobalUIClickHandler handler) { + globalHandlers.add(handler); + return handler.hashCode(); + } + + public void removeGlobalHandler(int hashCode) { + globalHandlers.removeIf(handler -> handler.hashCode() == hashCode); + } public int registerCurrentRoomHandler(RerenderScreen.ScreenPart screenPart, UIClickHandler uiClickHandler) { GameRoom currentRoom = gameState.getCurrentRoom(); @@ -35,19 +46,47 @@ public class UIClickHandlerRepository { } public boolean handleClick(MouseAction mouseAction) { - if (!roomSpecificHandlers.containsKey(gameState.getCurrentRoom())) { - return false; + if (roomSpecificHandlers.containsKey(gameState.getCurrentRoom())) { + Map handlers = roomSpecificHandlers.get(gameState.getCurrentRoom()); + + for (var entry : handlers.entrySet()) { + RerenderScreen.ScreenPart part = entry.getKey(); + UIClickHandler uiClickHandler = entry.getValue(); + TerminalPosition position = new TerminalPosition(mouseAction.getPosition().getColumn(), mouseAction.getPosition().getRow() * 2); + + if (part.isWithin(position)) { + uiClickHandler.handleClick(mouseAction); + return true; + } + } } - Map handlers = roomSpecificHandlers.get(gameState.getCurrentRoom()); + for (var handler : globalHandlers) { + if (handler.handleClick(mouseAction)) { + return true; + } + } - for (var entry : handlers.entrySet()) { - RerenderScreen.ScreenPart part = entry.getKey(); - UIClickHandler uiClickHandler = entry.getValue(); - TerminalPosition position = new TerminalPosition(mouseAction.getPosition().getColumn(), mouseAction.getPosition().getRow() * 2); + return false; + } - if (part.isWithin(position)) { - uiClickHandler.handleClick(mouseAction); + public boolean handleMove(MouseAction mouseAction) { + if (roomSpecificHandlers.containsKey(gameState.getCurrentRoom())) { + Map handlers = roomSpecificHandlers.get(gameState.getCurrentRoom()); + for (var entry : handlers.entrySet()) { + RerenderScreen.ScreenPart part = entry.getKey(); + UIClickHandler uiClickHandler = entry.getValue(); + TerminalPosition position = new TerminalPosition(mouseAction.getPosition().getColumn(), mouseAction.getPosition().getRow() * 2); + + if (part.isWithin(position)) { + uiClickHandler.handleMove(mouseAction); + return true; + } + } + } + + for (var handler : globalHandlers) { + if (handler.handleMove(mouseAction)) { return true; } } @@ -60,7 +99,7 @@ public class UIClickHandlerRepository { if (!roomSpecificHandlers.containsKey(currentRoom)) return; Map handlers = roomSpecificHandlers.get(currentRoom); - for (var key: handlers.keySet()) { + for (var key : handlers.keySet()) { if (key.hashCode() == screenPartHashCode) { handlers.remove(key, uiClickHandler); }