From 2bca24d6cdaf97c43940bf07acd2ee94b2b504d2 Mon Sep 17 00:00:00 2001 From: jzitnik-dev Date: Tue, 16 Dec 2025 22:40:54 +0100 Subject: [PATCH] feat: Handle small terminals --- .../jzitnik/events/TerminalTooSmallEvent.java | 6 ++ .../events/handlers/FullRoomDrawHandler.java | 82 ++++++++++--------- .../TerminalTooSmallEventHandler.java | 54 ++++++++++++ 3 files changed, 104 insertions(+), 38 deletions(-) create mode 100644 src/main/java/cz/jzitnik/events/TerminalTooSmallEvent.java create mode 100644 src/main/java/cz/jzitnik/events/handlers/TerminalTooSmallEventHandler.java diff --git a/src/main/java/cz/jzitnik/events/TerminalTooSmallEvent.java b/src/main/java/cz/jzitnik/events/TerminalTooSmallEvent.java new file mode 100644 index 0000000..79af32f --- /dev/null +++ b/src/main/java/cz/jzitnik/events/TerminalTooSmallEvent.java @@ -0,0 +1,6 @@ +package cz.jzitnik.events; + +import cz.jzitnik.utils.events.Event; + +public class TerminalTooSmallEvent implements Event { +} diff --git a/src/main/java/cz/jzitnik/events/handlers/FullRoomDrawHandler.java b/src/main/java/cz/jzitnik/events/handlers/FullRoomDrawHandler.java index 4cd0d8c..8974455 100644 --- a/src/main/java/cz/jzitnik/events/handlers/FullRoomDrawHandler.java +++ b/src/main/java/cz/jzitnik/events/handlers/FullRoomDrawHandler.java @@ -9,6 +9,7 @@ import cz.jzitnik.annotations.injectors.InjectDependency; import cz.jzitnik.annotations.injectors.InjectState; import cz.jzitnik.events.FullRoomDraw; import cz.jzitnik.events.RerenderScreen; +import cz.jzitnik.events.TerminalTooSmallEvent; import cz.jzitnik.game.GameRoom; import cz.jzitnik.game.GameState; import cz.jzitnik.game.Player; @@ -62,56 +63,61 @@ public class FullRoomDrawHandler extends AbstractEventHandler { @Override public void handle(FullRoomDraw event) { - log.debug("Rendering full room"); - TerminalScreen terminalScreen = terminalState.getTerminalScreen(); - List partsToRerender = new ArrayList<>(); - GameRoom currentRoom = gameState.getCurrentRoom(); + try { + log.debug("Rendering full room"); + TerminalScreen terminalScreen = terminalState.getTerminalScreen(); + List partsToRerender = new ArrayList<>(); + GameRoom currentRoom = gameState.getCurrentRoom(); - Set doorPositions = RerenderUtils.getDoorPositions(currentRoom); + Set doorPositions = RerenderUtils.getDoorPositions(currentRoom); - var buffer = screenBuffer.getRenderedBuffer(); - var overrideBuffer = currentRoom.getOverrideBuffer(); + var buffer = screenBuffer.getRenderedBuffer(); + var overrideBuffer = currentRoom.getOverrideBuffer(); - BufferedImage room = resourceManager.getResource(currentRoom.getTexture()); - BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS); - Player player = gameState.getPlayer(); - BufferedImage playerTexture = RerenderUtils.getPlayer(resourceManager, player); - TerminalSize terminalSize = terminalScreen.getTerminalSize(); + BufferedImage room = resourceManager.getResource(currentRoom.getTexture()); + BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS); + Player player = gameState.getPlayer(); + BufferedImage playerTexture = RerenderUtils.getPlayer(resourceManager, player); + TerminalSize terminalSize = terminalScreen.getTerminalSize(); - int width = room.getWidth(); - int height = room.getHeight(); + int width = room.getWidth(); + int height = room.getHeight(); - var start = RerenderUtils.getStart(room, terminalSize); - int startX = start.getX(); - int startY = start.getY(); + var start = RerenderUtils.getStart(room, terminalSize); + int startX = start.getX(); + int startY = start.getY(); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - RerenderUtils.PixelResult pixelResult = RerenderUtils.getPixel(currentRoom, room, doors, doorPositions, player, playerTexture, x, y); - int pixel = pixelResult.pixel(); - int red = (pixel >> 16) & 0xff; - int green = (pixel >> 8) & 0xff; - int blue = pixel & 0xff; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + RerenderUtils.PixelResult pixelResult = RerenderUtils.getPixel(currentRoom, room, doors, doorPositions, player, playerTexture, x, y); + int pixel = pixelResult.pixel(); + int red = (pixel >> 16) & 0xff; + int green = (pixel >> 8) & 0xff; + int blue = pixel & 0xff; - Pixel overridePixel = overrideBuffer[y][x]; - Pixel pixel1 = new ColoredPixel(new TextColor.RGB(red, green, blue)); + Pixel overridePixel = overrideBuffer[y][x]; + Pixel pixel1 = new ColoredPixel(new TextColor.RGB(red, green, blue)); - Pixel finalPixelLeft = overridePixel.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixel; + Pixel finalPixelLeft = overridePixel.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixel; - buffer[y + startY][x + startX] = finalPixelLeft; + buffer[y + startY][x + startX] = finalPixelLeft; + } } - } - partsToRerender.add(new RerenderScreen.ScreenPart( - new TerminalPosition(startX, startY), - new TerminalPosition(startX + width, startY + height - 1) - )); + partsToRerender.add(new RerenderScreen.ScreenPart( + new TerminalPosition(startX, startY), + new TerminalPosition(startX + width, startY + height - 1) + )); - if (renderState.isFirstRender() || event.isFullRerender()) { - eventManager.emitEvent(RerenderScreen.full(terminalSize)); - renderState.setFirstRender(false); - } else { - eventManager.emitEvent(new RerenderScreen(partsToRerender.toArray(RerenderScreen.ScreenPart[]::new))); + if (renderState.isFirstRender() || event.isFullRerender()) { + eventManager.emitEvent(RerenderScreen.full(terminalSize)); + renderState.setFirstRender(false); + } else { + eventManager.emitEvent(new RerenderScreen(partsToRerender.toArray(RerenderScreen.ScreenPart[]::new))); + } + } catch (ArrayIndexOutOfBoundsException e) { + // Screen too small to fit the room + eventManager.emitEvent(new TerminalTooSmallEvent()); } } } diff --git a/src/main/java/cz/jzitnik/events/handlers/TerminalTooSmallEventHandler.java b/src/main/java/cz/jzitnik/events/handlers/TerminalTooSmallEventHandler.java new file mode 100644 index 0000000..5ea0ea6 --- /dev/null +++ b/src/main/java/cz/jzitnik/events/handlers/TerminalTooSmallEventHandler.java @@ -0,0 +1,54 @@ +package cz.jzitnik.events.handlers; + +import com.googlecode.lanterna.SGR; +import com.googlecode.lanterna.TerminalSize; +import com.googlecode.lanterna.TextCharacter; +import com.googlecode.lanterna.TextColor; +import com.googlecode.lanterna.screen.Screen; +import com.googlecode.lanterna.screen.TerminalScreen; +import cz.jzitnik.annotations.EventHandler; +import cz.jzitnik.annotations.injectors.InjectState; +import cz.jzitnik.events.TerminalTooSmallEvent; +import cz.jzitnik.states.TerminalState; +import cz.jzitnik.utils.DependencyManager; +import cz.jzitnik.utils.events.AbstractEventHandler; + +import java.io.IOException; + +@EventHandler(TerminalTooSmallEvent.class) +public class TerminalTooSmallEventHandler extends AbstractEventHandler { + public TerminalTooSmallEventHandler(DependencyManager dm) { + super(dm); + } + + @InjectState + private TerminalState terminalState; + + @Override + public void handle(TerminalTooSmallEvent event) { + // Directly render message for the user + TerminalScreen terminalScreen = terminalState.getTerminalScreen(); + terminalScreen.clear(); + + TerminalSize terminalSize = terminalScreen.getTerminalSize(); + String text = "Terminal too small!"; + String subText = "Please resize your terminal"; + + int startY = terminalSize.getRows() / 2; + + int textStartX = (terminalSize.getColumns() / 2) - (text.length() / 2); + for (char character : text.toCharArray()) { + terminalScreen.setCharacter(textStartX++, startY, new TextCharacter(character, TextColor.ANSI.RED, TextColor.ANSI.DEFAULT, SGR.BOLD)); + } + int subTextStartX = (terminalSize.getColumns() / 2) - (subText.length() / 2); + for (char character : subText.toCharArray()) { + terminalScreen.setCharacter(subTextStartX++, startY + 1, new TextCharacter(character, TextColor.ANSI.BLACK_BRIGHT, TextColor.ANSI.DEFAULT)); + } + + try { + terminalScreen.refresh(Screen.RefreshType.COMPLETE); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +}