diff --git a/src/main/java/cz/jzitnik/Main.java b/src/main/java/cz/jzitnik/Main.java index 244a712..1f3bde6 100644 --- a/src/main/java/cz/jzitnik/Main.java +++ b/src/main/java/cz/jzitnik/Main.java @@ -1,8 +1,10 @@ package cz.jzitnik; import cz.jzitnik.game.Game; +import cz.jzitnik.game.MouseHandler; import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.tui.ScreenRenderer; +import org.jline.terminal.MouseEvent; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; @@ -11,18 +13,22 @@ import java.io.IOException; public class Main { public static void main(String[] args) { try { - // Set up terminal with JLine Terminal terminal = TerminalBuilder.terminal(); terminal.enterRawMode(); + terminal.trackMouse(Terminal.MouseTracking.Any); + + if (!terminal.hasMouseSupport()) { + System.out.println("Error: This terminal does not support mouse."); + System.exit(1); + } + var spriteList = SpriteLoader.load(); var screenRenderer = new ScreenRenderer(spriteList, terminal); var game = new Game(); - final boolean[] isRunning = {true}; + MouseHandler mouseHandler = new MouseHandler(game, terminal, screenRenderer); - // Enable mouse tracking - terminal.writer().print("\033[?1003h\n"); // Enable mouse move events - terminal.writer().flush(); + final boolean[] isRunning = {true}; Thread inputThread = new Thread(() -> { try { @@ -35,14 +41,8 @@ public class Main { if (key == '[') { key = terminal.reader().read(); if (key == 'M') { - int buttonCode = terminal.reader().read() - 32; - int x = terminal.reader().read() - 32; - int y = terminal.reader().read() - 32; - - if (buttonCode == 0) { - game.handleMouseClick(x, y); - //screenRenderer.render(game.getWorld()); - } + MouseEvent mouseEvent = terminal.readMouseEvent(); + mouseHandler.handle(mouseEvent); } } } @@ -60,9 +60,6 @@ public class Main { game.movePlayerUp(screenRenderer); screenRenderer.render(game.getWorld()); break; - case 'm': - game.mine(screenRenderer); - break; case 'q': System.out.println("Exiting game..."); isRunning[0] = false; @@ -89,8 +86,7 @@ public class Main { } // Disable mouse tracking on exit - terminal.writer().print("\033[?1003l\n"); - terminal.writer().flush(); + terminal.trackMouse(Terminal.MouseTracking.Off); terminal.close(); } catch (IOException e) { diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index 808bfa8..822be41 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -2,6 +2,7 @@ package cz.jzitnik.game; import cz.jzitnik.tui.ScreenRenderer; import lombok.Getter; + import java.util.ArrayList; import java.util.List; @@ -19,6 +20,7 @@ public class Game { } Block steveBlock = new Block("steve", SpriteLoader.SPRITES.STEVE); + world[9][0].add(new Block("grass", SpriteLoader.SPRITES.GRASS)); player = steveBlock; for (int i = 0; i < 50; i++) { @@ -45,7 +47,7 @@ public class Game { world[10][0].add(steveBlock); } - private int[] getPlayerCords() { + public int[] getPlayerCords() { for (int i = 0; i < world.length; i++) { for (int j = 0; j < world[i].length; j++) { for (Block block : world[i][j]) { @@ -148,15 +150,25 @@ public class Game { }).start(); } - public void mine(ScreenRenderer screenRenderer) { + public void mine(ScreenRenderer screenRenderer, int x, int y) { int[] cords = getPlayerCords(); - if (world[cords[1] + 1][cords[0]].stream().allMatch(Block::isGhost)) { + int playerX = cords[0]; + int playerY = cords[1]; + + int distanceX = Math.abs(playerX - x); + int distanceY = Math.abs(playerY - y); + + if (distanceX > 5 || distanceY > 5) { return; } - world[cords[1] + 1][cords[0]].clear(); - world[cords[1] + 1][cords[0]].add(new Block("air", SpriteLoader.SPRITES.AIR, true)); + if (world[y][x].stream().allMatch(Block::isGhost)) { + return; + } + + world[y][x].clear(); + world[y][x].add(new Block("air", SpriteLoader.SPRITES.AIR, true)); screenRenderer.render(world); new Thread(() -> { @@ -175,33 +187,4 @@ public class Game { } }).start(); } - - public void handleMouseClick(int mouseX, int mouseY) { - /*int[] cords = getPlayerCords(); - if (cords == null) return; - - int playerX = cords[0]; - int playerY = cords[1]; - int viewXRadius = 5; - int viewUpRadius = 4; - int viewDownRadius = 3; - - // Convert mouse coordinates to world coordinates - int blockX = startX + (mouseX / 50); // 50 chars wide per sprite - int blockY = startY + (mouseY / 25); // 25 lines high per sprite - - // Check if within bounds - if (blockY >= startY && blockY < endY && blockX >= startX && blockX < endX) { - List blocks = world[blockY][blockX]; - if (!blocks.isEmpty()) { - Block topBlock = blocks.get(blocks.size() - 1); // Get topmost block - System.out.println("Mouse clicked on block: " + topBlock.getBlockId() + - " at World Coordinates: [" + blockX + ", " + blockY + "]"); - } else { - System.out.println("No block at the clicked position."); - } - } else { - System.out.println("Clicked outside the visible area."); - }*/ - } } \ No newline at end of file diff --git a/src/main/java/cz/jzitnik/game/MouseHandler.java b/src/main/java/cz/jzitnik/game/MouseHandler.java new file mode 100644 index 0000000..6b84bae --- /dev/null +++ b/src/main/java/cz/jzitnik/game/MouseHandler.java @@ -0,0 +1,101 @@ +package cz.jzitnik.game; + +import cz.jzitnik.tui.ScreenMovingCalculationProvider; +import cz.jzitnik.tui.ScreenRenderer; +import lombok.AllArgsConstructor; +import org.jline.terminal.MouseEvent; +import org.jline.terminal.Terminal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@AllArgsConstructor +public class MouseHandler { + private Game game; + private Terminal terminal; + private ScreenRenderer screenRenderer; + + public void handle(MouseEvent mouseEvent) { + if (mouseEvent.getButton() == MouseEvent.Button.Button1 && mouseEvent.getType() == MouseEvent.Type.Pressed) { + leftClick(mouseEvent); + return; + } + + if (mouseEvent.getType() == MouseEvent.Type.Moved) { + move(mouseEvent); + } + } + + private void leftClick(MouseEvent mouseEvent) { + int mouseX = mouseEvent.getX(); + int mouseY = mouseEvent.getY(); + + int[] cords = game.getPlayerCords(); + if (cords == null) return; + + int playerX = cords[0]; + int playerY = cords[1]; + + int[] data = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length); + + int startX = data[0]; + int endX = data[1]; + int startY = data[2]; + int endY = data[3]; + + int blockX = startX + (mouseX / 50); // 50 chars wide per sprite + int blockY = startY + (mouseY / 25); // 25 lines high per sprite + + if (blockY == playerX && blockY == playerY) { + return; + } + + if (blockY >= startY && blockY < endY && blockX >= startX && blockX < endX) { + List blocks = game.getWorld()[blockY][blockX]; + System.out.println(blockX); + System.out.println(blockY); + if (!blocks.isEmpty()) { + game.mine(screenRenderer, blockX, blockY); + } + } + } + + private void move(MouseEvent mouseEvent) { + int mouseX = mouseEvent.getX(); + int mouseY = mouseEvent.getY(); + + int[] cords = game.getPlayerCords(); + if (cords == null) return; + + int playerX = cords[0]; + int playerY = cords[1]; + + int[] data = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length); + + int startX = data[0]; + int endX = data[1]; + int startY = data[2]; + int endY = data[3]; + + int blockX = startX + (mouseX / 50); // 50 chars wide per sprite + int blockY = startY + (mouseY / 25); // 25 lines high per sprite + + if (blockY == playerX && blockY == playerY) { + return; + } + + List blocks = game.getWorld()[blockY][blockX]; + + if (blockY >= startY && blockY < endY && blockX >= startX && blockX < endX && !blocks.stream().allMatch(block -> block.getBlockId().equals("air"))) { + List list = new ArrayList<>(); + list.add(blockX); + list.add(blockY); + screenRenderer.setSelectedBlock(Optional.of(list)); + } else { + screenRenderer.setSelectedBlock(Optional.empty()); + } + + screenRenderer.render(game.getWorld()); + } +} diff --git a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java index 85383c9..2b11fd9 100644 --- a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java +++ b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java @@ -1,16 +1,29 @@ package cz.jzitnik.tui; import cz.jzitnik.game.Block; -import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import org.jline.terminal.Terminal; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; -@AllArgsConstructor +@RequiredArgsConstructor public class ScreenRenderer { private SpriteList spriteList; private Terminal terminal; + public ScreenRenderer(SpriteList spriteList, Terminal terminal) { + this.spriteList = spriteList; + this.terminal = terminal; + } + + @Getter + @Setter + private Optional> selectedBlock = Optional.empty(); + private int[] getPlayerCords(List[][] world) { for (int i = 0; i < world.length; i++) { for (int j = 0; j < world[i].length; j++) { @@ -50,9 +63,9 @@ public class ScreenRenderer { if (visibleWidth % 2 != 0) { endX = Math.max(startX, endX - 1); } - /*if (visibleHeight % 2 != 0) { + if (visibleHeight % 2 != 0) { endY = Math.max(startY, endY - 1); - }*/ + } StringBuilder[] lines = new StringBuilder[(endY - startY) * 26]; for (int i = 0; i < lines.length; i++) { @@ -63,9 +76,36 @@ public class ScreenRenderer { for (int y = startY; y < endY; y++) { for (int x = startX; x < endX; x++) { List blocks = world[y][x]; - String sprite = SpriteCombiner.combineSprites(blocks.stream() - .map(block -> spriteList.getSprite(block.getSprite()).getSprite()) - .toArray(String[]::new)); + List sprites = new ArrayList<>(blocks.stream() + .map(block -> spriteList.getSprite(block.getSprite()).getSprite()).toList()); + + if (selectedBlock.isPresent() && selectedBlock.get().get(0) == x && selectedBlock.get().get(1) == y) { + StringBuilder stringBuilder = new StringBuilder(); + + for (int i = 0; i < 50; i++) { + stringBuilder.append("\033[38;5;231;48;5;231m▓"); + } + stringBuilder.append("\n"); + + for (int i = 0; i < 23; i++) { + stringBuilder.append("\033[38;5;231;48;5;231m▓"); + for (int j = 0; j < 48; j++) { + stringBuilder.append("\033[0m "); + } + stringBuilder.append("\033[38;5;231;48;5;231m▓"); + stringBuilder.append("\n"); + } + + for (int i = 0; i < 50; i++) { + stringBuilder.append("\033[38;5;231;48;5;231m▓"); + } + stringBuilder.append("\n"); + + sprites.add(stringBuilder.toString()); + } + + String sprite = SpriteCombiner.combineSprites(sprites.toArray(String[]::new)); + String[] spriteLines = sprite.split("\n");