From 91882fabc01aa3d9b726bbc23fcad3f237370b0c Mon Sep 17 00:00:00 2001 From: jzitnik-dev Date: Fri, 28 Feb 2025 13:56:55 +0100 Subject: [PATCH] feat: Implemented healthbar --- pom.xml | 12 +++ .../java/cz/jzitnik/game/Dependencies.java | 7 ++ src/main/java/cz/jzitnik/game/Game.java | 72 +++++++------- src/main/java/cz/jzitnik/game/GameStates.java | 2 + src/main/java/cz/jzitnik/game/Player.java | 13 +++ .../java/cz/jzitnik/game/SpriteLoader.java | 14 ++- .../annotations/RegisterPlaceHandler.java | 12 +++ .../game/crafting/CraftingRecipeList.java | 12 +++ .../game/generation/CaveGenerator.java | 12 +++ .../game/{ => generation}/Generation.java | 13 +-- .../handlers/place/CustomPlaceHandler.java | 8 ++ .../handlers/place/DefaultPlaceHandler.java | 24 +++++ .../game/handlers/place/PlaceHandler.java | 45 +++++++++ .../place/handlers/DoorPlaceHandler.java | 93 +++++++++++++++++++ .../rightclick}/RightClickHandler.java | 7 +- .../jzitnik/game/items/ItemBlockSupplier.java | 14 +++ .../java/cz/jzitnik/game/sprites/Heart.java | 25 +++++ .../java/cz/jzitnik/game/sprites/Hunger.java | 25 +++++ .../java/cz/jzitnik/game/sprites/OakDoor.java | 29 ++++++ .../java/cz/jzitnik/game/ui/Healthbar.java | 49 ++++++++++ .../tui/ScreenMovingCalculationProvider.java | 2 +- .../java/cz/jzitnik/tui/ScreenRenderer.java | 3 + src/main/java/cz/jzitnik/tui/SpriteList.java | 7 -- src/main/resources/textures/coal_ore.ans | 25 +++++ .../resources/textures/gui/heartempty.ans | 9 ++ src/main/resources/textures/gui/heartfull.ans | 9 ++ .../resources/textures/gui/hungerempty.ans | 9 ++ .../resources/textures/gui/hungerfull.ans | 9 ++ .../resources/textures/items/wooden_axe.ans | 51 +++++----- .../resources/textures/oak_door/bottom.ans | 25 +++++ .../textures/oak_door/bottomclosed.ans | 25 +++++ .../textures/oak_door/items/oak_door.ans | 25 +++++ src/main/resources/textures/oak_door/top.ans | 25 +++++ .../resources/textures/oak_door/topclosed.ans | 25 +++++ 34 files changed, 659 insertions(+), 78 deletions(-) create mode 100644 src/main/java/cz/jzitnik/game/Dependencies.java create mode 100644 src/main/java/cz/jzitnik/game/Player.java create mode 100644 src/main/java/cz/jzitnik/game/annotations/RegisterPlaceHandler.java create mode 100644 src/main/java/cz/jzitnik/game/generation/CaveGenerator.java rename src/main/java/cz/jzitnik/game/{ => generation}/Generation.java (91%) create mode 100644 src/main/java/cz/jzitnik/game/handlers/place/CustomPlaceHandler.java create mode 100644 src/main/java/cz/jzitnik/game/handlers/place/DefaultPlaceHandler.java create mode 100644 src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java create mode 100644 src/main/java/cz/jzitnik/game/handlers/place/handlers/DoorPlaceHandler.java rename src/main/java/cz/jzitnik/{tui => game/handlers/rightclick}/RightClickHandler.java (74%) create mode 100644 src/main/java/cz/jzitnik/game/sprites/Heart.java create mode 100644 src/main/java/cz/jzitnik/game/sprites/Hunger.java create mode 100644 src/main/java/cz/jzitnik/game/sprites/OakDoor.java create mode 100644 src/main/java/cz/jzitnik/game/ui/Healthbar.java create mode 100644 src/main/resources/textures/coal_ore.ans create mode 100644 src/main/resources/textures/gui/heartempty.ans create mode 100644 src/main/resources/textures/gui/heartfull.ans create mode 100644 src/main/resources/textures/gui/hungerempty.ans create mode 100644 src/main/resources/textures/gui/hungerfull.ans create mode 100644 src/main/resources/textures/oak_door/bottom.ans create mode 100644 src/main/resources/textures/oak_door/bottomclosed.ans create mode 100644 src/main/resources/textures/oak_door/items/oak_door.ans create mode 100644 src/main/resources/textures/oak_door/top.ans create mode 100644 src/main/resources/textures/oak_door/topclosed.ans diff --git a/pom.xml b/pom.xml index fc494f7..e9111aa 100644 --- a/pom.xml +++ b/pom.xml @@ -28,6 +28,18 @@ jline-reader 3.20.0 + + + org.reflections + reflections + 0.10.2 + + + + com.google.guava + guava + 31.1-jre + diff --git a/src/main/java/cz/jzitnik/game/Dependencies.java b/src/main/java/cz/jzitnik/game/Dependencies.java new file mode 100644 index 0000000..62e6837 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/Dependencies.java @@ -0,0 +1,7 @@ +package cz.jzitnik.game; + +import cz.jzitnik.game.handlers.place.PlaceHandler; + +public class Dependencies { + public PlaceHandler placeHandler = new PlaceHandler(); +} diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index 364c4c2..5b3675c 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -1,14 +1,16 @@ package cz.jzitnik.game; +import cz.jzitnik.game.generation.Generation; import cz.jzitnik.game.items.Item; import cz.jzitnik.game.items.ItemType; +import cz.jzitnik.game.handlers.place.CustomPlaceHandler; import cz.jzitnik.game.sprites.Breaking; import cz.jzitnik.game.sprites.Steve; import cz.jzitnik.game.ui.Chest; import cz.jzitnik.game.ui.Furnace; import cz.jzitnik.game.ui.Window; import cz.jzitnik.game.ui.Inventory; -import cz.jzitnik.tui.RightClickHandler; +import cz.jzitnik.game.handlers.rightclick.RightClickHandler; import cz.jzitnik.tui.ScreenMovingCalculationProvider; import cz.jzitnik.tui.ScreenRenderer; import lombok.Getter; @@ -20,11 +22,9 @@ import java.util.List; @Getter public class Game { - private List[][] world = new ArrayList[256][512]; - @Setter - private Block player; - @Setter - private Block player2; + @SuppressWarnings("unchecked") + private final List[][] world = (List[][]) new ArrayList[256][512]; + private final Player player = new Player(); private boolean mining = false; @Setter private Window window = Window.WORLD; @@ -61,10 +61,10 @@ public class Game { return; } - world[cords[1]][cords[0] + 1].add(player2); - world[cords[1]][cords[0]].remove(player2); - world[cords[1]-1][cords[0] + 1].add(player); - world[cords[1]-1][cords[0]].remove(player); + world[cords[1]][cords[0] + 1].add(player.getPlayerBlock2()); + world[cords[1]][cords[0]].remove(player.getPlayerBlock2()); + world[cords[1]-1][cords[0] + 1].add(player.getPlayerBlock1()); + world[cords[1]-1][cords[0]].remove(player.getPlayerBlock1()); screenRenderer.render(this); update(screenRenderer); @@ -80,10 +80,10 @@ public class Game { return; } - world[cords[1]][cords[0] - 1].add(player2); - world[cords[1]][cords[0]].remove(player2); - world[cords[1]-1][cords[0] - 1].add(player); - world[cords[1]-1][cords[0]].remove(player); + world[cords[1]][cords[0] - 1].add(player.getPlayerBlock2()); + world[cords[1]][cords[0]].remove(player.getPlayerBlock2()); + world[cords[1]-1][cords[0] - 1].add(player.getPlayerBlock1()); + world[cords[1]-1][cords[0]].remove(player.getPlayerBlock1()); screenRenderer.render(this); update(screenRenderer); @@ -95,14 +95,14 @@ public class Game { } int[] cords = getPlayerCords(); - if (world[cords[1] - 2][cords[0]].stream().anyMatch(block -> !block.isGhost()) || world[cords[1] + 1][cords[0]].stream().anyMatch(Block::isGhost)) { + if (world[cords[1] - 2][cords[0]].stream().anyMatch(block -> !block.isGhost()) || world[cords[1] + 1][cords[0]].stream().allMatch(Block::isGhost)) { return; } - world[cords[1] - 1][cords[0]].remove(player); - world[cords[1] - 1][cords[0]].add(player2); - world[cords[1] - 2][cords[0]].add(player); - world[cords[1]][cords[0]].remove(player2); + world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1()); + world[cords[1] - 1][cords[0]].add(player.getPlayerBlock2()); + world[cords[1] - 2][cords[0]].add(player.getPlayerBlock1()); + world[cords[1]][cords[0]].remove(player.getPlayerBlock2()); new Thread(() -> { try { @@ -113,10 +113,10 @@ public class Game { int[] cords2 = getPlayerCords(); if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) { - world[cords2[1] - 1][cords2[0]].remove(player); - world[cords2[1]][cords2[0]].add(player); - world[cords2[1] + 1][cords2[0]].add(player2); - world[cords2[1]][cords2[0]].remove(player2); + world[cords2[1] - 1][cords2[0]].remove(player.getPlayerBlock1()); + world[cords2[1]][cords2[0]].add(player.getPlayerBlock1()); + world[cords2[1] + 1][cords2[0]].add(player.getPlayerBlock2()); + world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2()); screenRenderer.render(this); } @@ -132,7 +132,7 @@ public class Game { world[y][x].add(breakingBlock); screenRenderer.render(this); - double hardness = world[y][x].stream().filter(block -> !block.isGhost()).toList().get(0).calculateHardness(inventory); + double hardness = world[y][x].stream().filter(block -> !block.isGhost()).toList().getFirst().calculateHardness(inventory); this.mining = true; @@ -187,9 +187,10 @@ public class Game { } } - blocks.clear(); - blocks.add(new Block("air", SpriteLoader.SPRITES.AIR, true, false)); + CustomPlaceHandler customPlaceHandler = gameStates.dependencies.placeHandler.get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId()); + customPlaceHandler.mine(this, x, y); inventory.getItemInHand().ifPresent(Item::use); + screenRenderer.render(this); update(screenRenderer); @@ -233,10 +234,10 @@ public class Game { int[] cords2 = getPlayerCords(); if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) { - world[cords2[1] - 1][cords2[0]].remove(player); - world[cords2[1]][cords2[0]].add(player); - world[cords2[1] + 1][cords2[0]].add(player2); - world[cords2[1]][cords2[0]].remove(player2); + world[cords2[1] - 1][cords2[0]].remove(player.getPlayerBlock1()); + world[cords2[1]][cords2[0]].add(player.getPlayerBlock1()); + world[cords2[1] + 1][cords2[0]].add(player.getPlayerBlock2()); + world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2()); screenRenderer.render(this); } else { @@ -264,7 +265,7 @@ public class Game { } if (!blocks.stream().allMatch(block -> block.getBlockId().equals("air"))) { - RightClickHandler.handle(x, y, this); + RightClickHandler.handle(x, y, this, screenRenderer); screenRenderer.render(this); return; } @@ -273,12 +274,13 @@ public class Game { return; } - blocks.removeAll(blocks.stream().filter(block -> block.getBlockId().equals("air")).toList()); - blocks.add(inventory.getItemInHand().get().getBlock().get()); + Item item = inventory.getItemInHand().get(); - inventory.decreaseItemInHand(); + CustomPlaceHandler placeHandler = gameStates.dependencies.placeHandler.get(item.getId()); - screenRenderer.render(this); + if (placeHandler.place(this, x, y)) { + screenRenderer.render(this); + } } public void changeSlot(int slot, ScreenRenderer screenRenderer) { diff --git a/src/main/java/cz/jzitnik/game/GameStates.java b/src/main/java/cz/jzitnik/game/GameStates.java index c4fa272..b10d2b1 100644 --- a/src/main/java/cz/jzitnik/game/GameStates.java +++ b/src/main/java/cz/jzitnik/game/GameStates.java @@ -4,10 +4,12 @@ import cz.jzitnik.game.ui.CraftingTable; public class GameStates { public CraftingTable craftingTable; + public Dependencies dependencies; public int clickX = -1; public int clickY = -1; public GameStates(Game game) { craftingTable = new CraftingTable(game); + dependencies = new Dependencies(); } } diff --git a/src/main/java/cz/jzitnik/game/Player.java b/src/main/java/cz/jzitnik/game/Player.java new file mode 100644 index 0000000..e496c28 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/Player.java @@ -0,0 +1,13 @@ +package cz.jzitnik.game; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Player { + private int health = 10; + private int hunger = 6; + private Block playerBlock1; + private Block playerBlock2; +} diff --git a/src/main/java/cz/jzitnik/game/SpriteLoader.java b/src/main/java/cz/jzitnik/game/SpriteLoader.java index 06d41ff..f653adb 100644 --- a/src/main/java/cz/jzitnik/game/SpriteLoader.java +++ b/src/main/java/cz/jzitnik/game/SpriteLoader.java @@ -21,10 +21,12 @@ public class SpriteLoader { CHEST, CRAFTING_TABLE, + COAL_ORE, OAK_LOG, OAK_LEAF, OAK_PLANKS, + OAK_DOOR, WOODEN_PICKAXE, STONE_PICKAXE, @@ -41,9 +43,13 @@ public class SpriteLoader { ITEM_COBBLESTONE, ITEM_STONE, ITEM_FURNACE, + ITEM_OAK_DOOR, ITEM_CRAFTING_TABLE, - ITEM_CHEST + ITEM_CHEST, + + HEART, + HUNGER } public static final HashMap SPRITES_MAP = new HashMap<>(); @@ -63,6 +69,8 @@ public class SpriteLoader { SPRITES_MAP.put(SPRITES.CHEST, new SimpleSprite("chest.ans")); SPRITES_MAP.put(SPRITES.COBBLESTONE, new SimpleSprite("cobblestone.ans")); SPRITES_MAP.put(SPRITES.FURNACE, new Furnace()); + SPRITES_MAP.put(SPRITES.COAL_ORE, new SimpleSprite("coal_ore.ans")); + SPRITES_MAP.put(SPRITES.OAK_DOOR, new OakDoor()); SPRITES_MAP.put(SPRITES.WOODEN_PICKAXE, new SimpleSprite("items/wooden_pickaxe.ans")); SPRITES_MAP.put(SPRITES.STONE_PICKAXE, new SimpleSprite("items/stone_pickaxe.ans")); @@ -79,6 +87,10 @@ public class SpriteLoader { SPRITES_MAP.put(SPRITES.ITEM_STONE, new SimpleSprite("items/stone.ans")); SPRITES_MAP.put(SPRITES.ITEM_CHEST, new SimpleSprite("items/chest.ans")); SPRITES_MAP.put(SPRITES.ITEM_FURNACE, new SimpleSprite("items/furnace.ans")); + SPRITES_MAP.put(SPRITES.ITEM_OAK_DOOR, new SimpleSprite("oak_door/items/oak_door.ans")); + + SPRITES_MAP.put(SPRITES.HEART, new Heart()); + SPRITES_MAP.put(SPRITES.HUNGER, new Hunger()); } public static SpriteList load() { diff --git a/src/main/java/cz/jzitnik/game/annotations/RegisterPlaceHandler.java b/src/main/java/cz/jzitnik/game/annotations/RegisterPlaceHandler.java new file mode 100644 index 0000000..d9cc272 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/RegisterPlaceHandler.java @@ -0,0 +1,12 @@ +package cz.jzitnik.game.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface RegisterPlaceHandler { + String value(); +} diff --git a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java index 9b3c867..f03a109 100644 --- a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java +++ b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java @@ -94,6 +94,18 @@ public class CraftingRecipeList { {"cobblestone", null, "cobblestone"}, {"cobblestone", "cobblestone", "cobblestone"} }, () -> new InventoryItem(1, ItemBlockSupplier.Items.furnace()))); + + recipes.add(new CraftingRecipe(new String[][]{ + {"oak_planks", "oak_planks", null}, + {"oak_planks", "oak_planks", null}, + {"oak_planks", "oak_planks", null} + }, () -> new InventoryItem(1, ItemBlockSupplier.Items.oakDoor()))); + + recipes.add(new CraftingRecipe(new String[][]{ + {"dirt", null, null}, + {null, null, null}, + {null, null, null} + }, () -> new InventoryItem(1, ItemBlockSupplier.Items.oakDoor()))); } public static Optional getRecipe(String[] r) { diff --git a/src/main/java/cz/jzitnik/game/generation/CaveGenerator.java b/src/main/java/cz/jzitnik/game/generation/CaveGenerator.java new file mode 100644 index 0000000..a27c98f --- /dev/null +++ b/src/main/java/cz/jzitnik/game/generation/CaveGenerator.java @@ -0,0 +1,12 @@ +package cz.jzitnik.game.generation; + +import cz.jzitnik.game.Block; +import java.util.List; + +public class CaveGenerator { + private static final int WIDTH = 512; + private static final int HEIGHT = 256; + + public static void generateCaves(List[][] world, int[] terrainHeight) { + } +} diff --git a/src/main/java/cz/jzitnik/game/Generation.java b/src/main/java/cz/jzitnik/game/generation/Generation.java similarity index 91% rename from src/main/java/cz/jzitnik/game/Generation.java rename to src/main/java/cz/jzitnik/game/generation/Generation.java index 9e9b50c..7798e79 100644 --- a/src/main/java/cz/jzitnik/game/Generation.java +++ b/src/main/java/cz/jzitnik/game/generation/Generation.java @@ -1,5 +1,8 @@ -package cz.jzitnik.game; +package cz.jzitnik.game.generation; +import cz.jzitnik.game.Block; +import cz.jzitnik.game.Game; +import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.items.ItemBlockSupplier; import cz.jzitnik.game.items.ItemType; import cz.jzitnik.game.sprites.Steve; @@ -18,8 +21,8 @@ public class Generation { Block steveBlock2 = new Block("steve", SpriteLoader.SPRITES.STEVE); steveBlock2.setSpriteState(Steve.SteveState.SECOND); - game.setPlayer(steveBlock); - game.setPlayer2(steveBlock2); + game.getPlayer().setPlayerBlock1(steveBlock); + game.getPlayer().setPlayerBlock2(steveBlock2); int[] terrainHeight = generateTerrain(); populateWorld(world, terrainHeight); @@ -77,9 +80,7 @@ public class Generation { for (List[] lists : world) { for (List list : lists) { - if (list.isEmpty()) { - list.add(new Block("air", SpriteLoader.SPRITES.AIR, true, false)); - } + list.addFirst(new Block("air", SpriteLoader.SPRITES.AIR, true, false)); } } } diff --git a/src/main/java/cz/jzitnik/game/handlers/place/CustomPlaceHandler.java b/src/main/java/cz/jzitnik/game/handlers/place/CustomPlaceHandler.java new file mode 100644 index 0000000..f0c1645 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/handlers/place/CustomPlaceHandler.java @@ -0,0 +1,8 @@ +package cz.jzitnik.game.handlers.place; + +import cz.jzitnik.game.Game; + +public interface CustomPlaceHandler { + boolean place(Game game, int x, int y); + void mine(Game game, int x, int y); +} diff --git a/src/main/java/cz/jzitnik/game/handlers/place/DefaultPlaceHandler.java b/src/main/java/cz/jzitnik/game/handlers/place/DefaultPlaceHandler.java new file mode 100644 index 0000000..2d8681a --- /dev/null +++ b/src/main/java/cz/jzitnik/game/handlers/place/DefaultPlaceHandler.java @@ -0,0 +1,24 @@ +package cz.jzitnik.game.handlers.place; + +import cz.jzitnik.game.Game; + +public class DefaultPlaceHandler implements CustomPlaceHandler { + @Override + public boolean place(Game game, int x, int y) { + var blocks = game.getWorld()[y][x]; + var inventory = game.getInventory(); + + blocks.add(inventory.getItemInHand().get().getBlock().get()); + + inventory.decreaseItemInHand(); + + return true; + } + + @Override + public void mine(Game game, int x, int y) { + var blocks = game.getWorld()[y][x]; + + blocks.removeAll(blocks.stream().filter(i -> !i.getBlockId().equals("air")).toList()); + } +} diff --git a/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java b/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java new file mode 100644 index 0000000..6c5d650 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java @@ -0,0 +1,45 @@ +package cz.jzitnik.game.handlers.place; + +import java.util.HashMap; +import java.util.Set; + +import cz.jzitnik.game.annotations.RegisterPlaceHandler; +import org.reflections.Reflections; + +public class PlaceHandler { + private final HashMap placeHandlerList = new HashMap<>(); + private final CustomPlaceHandler defaultPlaceHandler = new DefaultPlaceHandler(); + + public boolean contains(String itemId) { + return placeHandlerList.containsKey(itemId); + } + + public CustomPlaceHandler get(String itemId) { + if (!contains(itemId)) { + return defaultPlaceHandler; + } + + return placeHandlerList.get(itemId); + } + + public PlaceHandler() { + registerHandlers(); + } + + private void registerHandlers() { + Reflections reflections = new Reflections("cz.jzitnik.game.handlers.place.handlers"); + Set> handlerClasses = reflections.getTypesAnnotatedWith(RegisterPlaceHandler.class); + + for (Class clazz : handlerClasses) { + if (CustomPlaceHandler.class.isAssignableFrom(clazz)) { + try { + CustomPlaceHandler handlerInstance = (CustomPlaceHandler) clazz.getDeclaredConstructor().newInstance(); + RegisterPlaceHandler annotation = clazz.getAnnotation(RegisterPlaceHandler.class); + placeHandlerList.put(annotation.value(), handlerInstance); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/cz/jzitnik/game/handlers/place/handlers/DoorPlaceHandler.java b/src/main/java/cz/jzitnik/game/handlers/place/handlers/DoorPlaceHandler.java new file mode 100644 index 0000000..cd1672a --- /dev/null +++ b/src/main/java/cz/jzitnik/game/handlers/place/handlers/DoorPlaceHandler.java @@ -0,0 +1,93 @@ +package cz.jzitnik.game.handlers.place.handlers; + +import cz.jzitnik.game.Block; +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.RegisterPlaceHandler; +import cz.jzitnik.game.handlers.place.CustomPlaceHandler; +import cz.jzitnik.game.items.ItemBlockSupplier; +import cz.jzitnik.game.sprites.OakDoor; +import cz.jzitnik.tui.ScreenRenderer; + +@RegisterPlaceHandler("oak_door") +public class DoorPlaceHandler implements CustomPlaceHandler { + public static void rightClick(Game game, int x, int y, ScreenRenderer screenRenderer) { + var blocks = game.getWorld()[y][x]; + var door = blocks.stream().filter(block -> block.getBlockId().equals("oak_door")).toList().getFirst(); + + + switch (door.getSpriteState().get()) { + case OakDoor.OakDoorState.TOP, OakDoor.OakDoorState.TOPCLOSED -> { + var blocks2 = game.getWorld()[y+1][x]; + var door2 = blocks2.stream().filter(block -> block.getBlockId().equals("oak_door")).toList().getFirst(); + change(door2); + } + case OakDoor.OakDoorState.BOTTOM, OakDoor.OakDoorState.BOTTOMCLOSED -> { + var blocks2 = game.getWorld()[y-1][x]; + var door2 = blocks2.stream().filter(block -> block.getBlockId().equals("oak_door")).toList().getFirst(); + change(door2); + } + default -> throw new IllegalStateException("Unexpected value: " + door.getSpriteState().get()); + } + + change(door); + game.update(screenRenderer); + } + + private static void change(Block door) { + door.setSpriteState(switch (door.getSpriteState().get()) { + case OakDoor.OakDoorState.TOP -> OakDoor.OakDoorState.TOPCLOSED; + case OakDoor.OakDoorState.BOTTOM -> OakDoor.OakDoorState.BOTTOMCLOSED; + case OakDoor.OakDoorState.TOPCLOSED -> OakDoor.OakDoorState.TOP; + case OakDoor.OakDoorState.BOTTOMCLOSED -> OakDoor.OakDoorState.BOTTOM; + default -> throw new IllegalStateException("Unexpected value: " + door.getSpriteState().get()); + }); + + door.setGhost(switch (door.getSpriteState().get()) { + case OakDoor.OakDoorState.TOP, OakDoor.OakDoorState.BOTTOM -> true; + case OakDoor.OakDoorState.TOPCLOSED, OakDoor.OakDoorState.BOTTOMCLOSED -> false; + default -> throw new IllegalStateException("Unexpected value: " + door.getSpriteState().get()); + }); + } + + @Override + public boolean place(Game game, int x, int y) { + var blocks = game.getWorld()[y][x]; + var blocksTop = game.getWorld()[y-1][x]; + + if (!blocksTop.stream().allMatch(Block::isGhost)) { + return false; + } + + var inventory = game.getInventory(); + + Block block = inventory.getItemInHand().get().getBlock().get(); + block.setSpriteState(OakDoor.OakDoorState.BOTTOMCLOSED); + blocks.add(block); + + Block block2 = ItemBlockSupplier.Blocks.oakDoor(); + block2.setSpriteState(OakDoor.OakDoorState.TOPCLOSED); + blocksTop.add(block2); + + inventory.decreaseItemInHand(); + + return true; + } + + @Override + public void mine(Game game, int x, int y) { + var blocks = game.getWorld()[y][x]; + Block block = blocks.stream().filter(b -> b.getBlockId().equals("oak_door")).toList().getFirst(); + + if (block.getSpriteState().get() == OakDoor.OakDoorState.BOTTOM || block.getSpriteState().get() == OakDoor.OakDoorState.BOTTOMCLOSED) { + var blocks2 = game.getWorld()[y-1][x]; + blocks2.removeAll(blocks2.stream().filter(i -> !i.getBlockId().equals("air")).toList()); + } + + if (block.getSpriteState().get() == OakDoor.OakDoorState.TOP || block.getSpriteState().get() == OakDoor.OakDoorState.TOPCLOSED) { + var blocks2 = game.getWorld()[y+1][x]; + blocks2.removeAll(blocks2.stream().filter(i -> !i.getBlockId().equals("air")).toList()); + } + + blocks.removeAll(blocks.stream().filter(i -> !i.getBlockId().equals("air")).toList()); + } +} diff --git a/src/main/java/cz/jzitnik/tui/RightClickHandler.java b/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandler.java similarity index 74% rename from src/main/java/cz/jzitnik/tui/RightClickHandler.java rename to src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandler.java index 1e24353..557f722 100644 --- a/src/main/java/cz/jzitnik/tui/RightClickHandler.java +++ b/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandler.java @@ -1,8 +1,10 @@ -package cz.jzitnik.tui; +package cz.jzitnik.game.handlers.rightclick; import cz.jzitnik.game.Block; import cz.jzitnik.game.Game; +import cz.jzitnik.game.handlers.place.handlers.DoorPlaceHandler; import cz.jzitnik.game.ui.Window; +import cz.jzitnik.tui.ScreenRenderer; import java.util.HashMap; @@ -12,7 +14,7 @@ public class RightClickHandler { void apply(T t, U u); } - public static void handle(int x, int y, Game game) { + public static void handle(int x, int y, Game game, ScreenRenderer screenRenderer) { if (game.isMining()) { return; } @@ -21,6 +23,7 @@ public class RightClickHandler { functionMap.put("crafting_table", game.getGameStates().craftingTable::render); functionMap.put("chest", (Integer ignored, Integer ignored2) -> game.setWindow(Window.CHEST)); functionMap.put("furnace", (Integer ignored, Integer ignored2) -> game.setWindow(Window.FURNACE)); + functionMap.put("oak_door", (Integer xx, Integer xy) -> DoorPlaceHandler.rightClick(game, xx, xy, screenRenderer)); game.getGameStates().clickX = x; game.getGameStates().clickY = y; diff --git a/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java b/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java index bc6ef14..b03aa33 100644 --- a/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java +++ b/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java @@ -36,6 +36,9 @@ public class ItemBlockSupplier { public static Item furnace(Block ref) { return new Item("furnace", "Furnace", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_FURNACE, ref); } + public static Item oakDoor(Block ref) { + return new Item("oak_door", "Oak door", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_OAK_DOOR, ref); + } } public static class Blocks { @@ -85,6 +88,14 @@ public class ItemBlockSupplier { block.setDrops(List.of(Helper.furnace(block))); return block; } + public static Block coalOre() { + return new Block("coal_ore", SpriteLoader.SPRITES.COAL_ORE); + } + public static Block oakDoor() { + var block = new Block("oak_door", SpriteLoader.SPRITES.OAK_DOOR, 3, ItemType.AXE, new ArrayList<>()); + block.setDrops(List.of(Helper.oakDoor(block))); + return block; + } } // I hate this but whatever @@ -131,5 +142,8 @@ public class ItemBlockSupplier { public static Item furnace() { return Helper.furnace(Blocks.furnace()); } + public static Item oakDoor() { + return Helper.oakDoor(Blocks.oakDoor()); + } } } diff --git a/src/main/java/cz/jzitnik/game/sprites/Heart.java b/src/main/java/cz/jzitnik/game/sprites/Heart.java new file mode 100644 index 0000000..5b3ca49 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/sprites/Heart.java @@ -0,0 +1,25 @@ +package cz.jzitnik.game.sprites; + +import cz.jzitnik.tui.ResourceLoader; +import cz.jzitnik.tui.Sprite; + +public class Heart extends Sprite { + public enum HeartState { + OFF, + ON, + } + + public String getSprite() { + throw new RuntimeException("Idk"); + } + + public String getSprite(Enum e) { + return ResourceLoader.loadResource( + switch (e) { + case HeartState.OFF -> "gui/heartempty.ans"; + case HeartState.ON -> "gui/heartfull.ans"; + default -> throw new IllegalStateException("Unexpected value: " + e); + } + ); + } +} diff --git a/src/main/java/cz/jzitnik/game/sprites/Hunger.java b/src/main/java/cz/jzitnik/game/sprites/Hunger.java new file mode 100644 index 0000000..979e6c2 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/sprites/Hunger.java @@ -0,0 +1,25 @@ +package cz.jzitnik.game.sprites; + +import cz.jzitnik.tui.ResourceLoader; +import cz.jzitnik.tui.Sprite; + +public class Hunger extends Sprite { + public enum HungerState { + OFF, + ON, + } + + public String getSprite() { + throw new RuntimeException("Idk"); + } + + public String getSprite(Enum e) { + return ResourceLoader.loadResource( + switch (e) { + case HungerState.OFF -> "gui/hungerempty.ans"; + case HungerState.ON -> "gui/hungerfull.ans"; + default -> throw new IllegalStateException("Unexpected value: " + e); + } + ); + } +} diff --git a/src/main/java/cz/jzitnik/game/sprites/OakDoor.java b/src/main/java/cz/jzitnik/game/sprites/OakDoor.java new file mode 100644 index 0000000..aa948a5 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/sprites/OakDoor.java @@ -0,0 +1,29 @@ +package cz.jzitnik.game.sprites; + +import cz.jzitnik.tui.ResourceLoader; +import cz.jzitnik.tui.Sprite; + +public class OakDoor extends Sprite { + public enum OakDoorState { + TOP, + BOTTOM, + TOPCLOSED, + BOTTOMCLOSED + } + + public String getSprite() { + return ResourceLoader.loadResource("oak_door/bottomclosed.ans"); + } + + public String getSprite(Enum e) { + return ResourceLoader.loadResource( + switch (e) { + case OakDoorState.TOP -> "oak_door/top.ans"; + case OakDoorState.BOTTOM -> "oak_door/bottom.ans"; + case OakDoorState.TOPCLOSED -> "oak_door/topclosed.ans"; + case OakDoorState.BOTTOMCLOSED -> "oak_door/bottomclosed.ans"; + default -> throw new IllegalStateException("Unexpected value: " + e); + } + ); + } +} diff --git a/src/main/java/cz/jzitnik/game/ui/Healthbar.java b/src/main/java/cz/jzitnik/game/ui/Healthbar.java new file mode 100644 index 0000000..dcd8105 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/ui/Healthbar.java @@ -0,0 +1,49 @@ +package cz.jzitnik.game.ui; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.SpriteLoader; +import cz.jzitnik.game.sprites.Heart; +import cz.jzitnik.game.sprites.Hunger; +import cz.jzitnik.tui.SpriteList; +import org.jline.terminal.Terminal; + +import static cz.jzitnik.game.ui.Inventory.INVENTORY_SIZE_PX; + +public class Healthbar { + public static void render(StringBuilder buffer, SpriteList spriteList, Terminal terminal, Game game) { + int termWidth = terminal.getWidth(); + int startLeft = (termWidth / 2) - (INVENTORY_SIZE_PX / 2); + + int heartSize = 9 * 20; + int moveLeft = INVENTORY_SIZE_PX - (heartSize * 2); + + String[] spriteOn = spriteList.getSprite(SpriteLoader.SPRITES.HEART).getSprite(Heart.HeartState.ON).split("\n"); + String[] spriteOff = spriteList.getSprite(SpriteLoader.SPRITES.HEART).getSprite(Heart.HeartState.OFF).split("\n"); + + String[] hungerSpriteOn = spriteList.getSprite(SpriteLoader.SPRITES.HUNGER).getSprite(Hunger.HungerState.ON).split("\n"); + String[] hungerSpriteOff = spriteList.getSprite(SpriteLoader.SPRITES.HUNGER).getSprite(Hunger.HungerState.OFF).split("\n"); + + for (int i = 0; i < 9; i++) { + buffer.append(" ".repeat(startLeft)); + for (int j = 0; j < 10; j++) { + if (j < game.getPlayer().getHealth()) { + buffer.append(spriteOn[i]); + } else { + buffer.append(spriteOff[i]); + } + } + + buffer.append("\033[0m").append(" ".repeat(moveLeft)); + for (int j = 0; j < 10; j++) { + if ((10 - j) <= game.getPlayer().getHunger()) { + buffer.append(hungerSpriteOn[i]); + } else { + buffer.append(hungerSpriteOff[i]); + } + } + + buffer.append("\n"); + } + buffer.append("\n\n"); + } +} diff --git a/src/main/java/cz/jzitnik/tui/ScreenMovingCalculationProvider.java b/src/main/java/cz/jzitnik/tui/ScreenMovingCalculationProvider.java index 4e8f636..446976a 100644 --- a/src/main/java/cz/jzitnik/tui/ScreenMovingCalculationProvider.java +++ b/src/main/java/cz/jzitnik/tui/ScreenMovingCalculationProvider.java @@ -6,7 +6,7 @@ public class ScreenMovingCalculationProvider { int spriteHeight = 25; int viewXRadius = (terminalWidth / 2) / spriteWidth; - int viewYRadius = ((terminalHeight - 30) / 2) / spriteHeight; + int viewYRadius = ((terminalHeight - 30 - 9) / 2) / spriteHeight; // Ensure at least one sprite is visible viewXRadius = Math.max(viewXRadius, 1); diff --git a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java index 6bdde91..7ed0015 100644 --- a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java +++ b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java @@ -5,6 +5,7 @@ import cz.jzitnik.game.Game; import cz.jzitnik.game.sprites.Steve; import cz.jzitnik.game.ui.Chest; import cz.jzitnik.game.ui.Furnace; +import cz.jzitnik.game.ui.Healthbar; import cz.jzitnik.tui.utils.SpriteCombiner; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -141,6 +142,8 @@ public class ScreenRenderer { // Empty space between world and hotbar main.append("\n\n\n"); + Healthbar.render(main, spriteList, terminal, game); + game.getInventory().renderHotbar(main, spriteList, terminal, false); } } diff --git a/src/main/java/cz/jzitnik/tui/SpriteList.java b/src/main/java/cz/jzitnik/tui/SpriteList.java index c482847..f788bdb 100644 --- a/src/main/java/cz/jzitnik/tui/SpriteList.java +++ b/src/main/java/cz/jzitnik/tui/SpriteList.java @@ -23,11 +23,4 @@ public class SpriteList> { public Sprite getSprite(E key) { return sprites.get(key); } - - public void setSprite(E key, Sprite value) { - if (!sprites.containsKey(key)) { - throw new IllegalArgumentException("Invalid key: " + key); - } - sprites.put(key, value); - } } diff --git a/src/main/resources/textures/coal_ore.ans b/src/main/resources/textures/coal_ore.ans new file mode 100644 index 0000000..a1e1db3 --- /dev/null +++ b/src/main/resources/textures/coal_ore.ans @@ -0,0 +1,25 @@ +            +        +                 +                +              +            +        +                +               +              +                +      +              +           +          +               +            +             +            +             +             +             +          +        +        diff --git a/src/main/resources/textures/gui/heartempty.ans b/src/main/resources/textures/gui/heartempty.ans new file mode 100644 index 0000000..5233123 --- /dev/null +++ b/src/main/resources/textures/gui/heartempty.ans @@ -0,0 +1,9 @@ +               +            +          +          +          +            +              +                +                  diff --git a/src/main/resources/textures/gui/heartfull.ans b/src/main/resources/textures/gui/heartfull.ans new file mode 100644 index 0000000..6f4b7bd --- /dev/null +++ b/src/main/resources/textures/gui/heartfull.ans @@ -0,0 +1,9 @@ +               +            +          +          +          +            +              +                +                  diff --git a/src/main/resources/textures/gui/hungerempty.ans b/src/main/resources/textures/gui/hungerempty.ans new file mode 100644 index 0000000..e1707ce --- /dev/null +++ b/src/main/resources/textures/gui/hungerempty.ans @@ -0,0 +1,9 @@ +                +               +             +            +             +              +             +                +                diff --git a/src/main/resources/textures/gui/hungerfull.ans b/src/main/resources/textures/gui/hungerfull.ans new file mode 100644 index 0000000..3d1e651 --- /dev/null +++ b/src/main/resources/textures/gui/hungerfull.ans @@ -0,0 +1,9 @@ +                 +               +             +            +             +             +             +                +                diff --git a/src/main/resources/textures/items/wooden_axe.ans b/src/main/resources/textures/items/wooden_axe.ans index ba5932a..7a1cbf6 100644 --- a/src/main/resources/textures/items/wooden_axe.ans +++ b/src/main/resources/textures/items/wooden_axe.ans @@ -1,26 +1,25 @@ -                                                   -                                                   -                            ▒                      -                         ░░░░      ░░░             -                            ░░░░░░░                -                         ░░░░░░░░░░                -                         ░░░░░░░░░░                -                      ░░░░░░░░░░░░░   ░░░          -                      ░░░░░░░░░░░░░░░░             -                      ░░░░░░░░░░░░░░░░             -                                ░░░░░░░░░          -                            ░░░░   ░░░░░░          -                            ░░░░   ░░░░░░          -                         ░░░   ▒                   -                   ▓▓▓░░░    ░░░   ░░░░░░          -                      ░░░   ░                      -                   ░░░                             -                   ░░░                             -                ░░░                                -             ░░░                                   -             ░░░                                   -      ░   ░░░                                      -      ░                                            -      ░                                            -                                                   - \ No newline at end of file +                                                   +                                              +                                                +                                            +                                           +                                           +                                       +                                      +                                        +                                        +                                       +                                        +                                        +                                           +                                              +                                              +                                              +                                              +                                              +                                               +                                             +                                              +                                               +                                               +                                                   diff --git a/src/main/resources/textures/oak_door/bottom.ans b/src/main/resources/textures/oak_door/bottom.ans new file mode 100644 index 0000000..e0f9573 --- /dev/null +++ b/src/main/resources/textures/oak_door/bottom.ans @@ -0,0 +1,25 @@ +                     +                      +                    +                   +                 +                        +                    +                 +                      +                       +                    +                      +                         +              +                  +                     +                   +                  +                        +                 +                       +                    +                    +                 +    diff --git a/src/main/resources/textures/oak_door/bottomclosed.ans b/src/main/resources/textures/oak_door/bottomclosed.ans new file mode 100644 index 0000000..1de16c8 --- /dev/null +++ b/src/main/resources/textures/oak_door/bottomclosed.ans @@ -0,0 +1,25 @@ +                                              +                                              +                                              +                                              +                                              +                                             +                                             +                                              +                                              +                                              +                                              +                                              +                                              +                                              +                                              +                                             +                                              +                                              +                                              +                                              +                                              +                                              +                                             +                                            +                                            diff --git a/src/main/resources/textures/oak_door/items/oak_door.ans b/src/main/resources/textures/oak_door/items/oak_door.ans new file mode 100644 index 0000000..46d42cd --- /dev/null +++ b/src/main/resources/textures/oak_door/items/oak_door.ans @@ -0,0 +1,25 @@ +                                                   +                                                   +                                                   +                      +                      +                                           +                                           +                                           +                        +                           +                                           +                                           +                              +                          +                            +                              +                               +                           +                      +                           +                               +                               +                      +                      +                     diff --git a/src/main/resources/textures/oak_door/top.ans b/src/main/resources/textures/oak_door/top.ans new file mode 100644 index 0000000..b960535 --- /dev/null +++ b/src/main/resources/textures/oak_door/top.ans @@ -0,0 +1,25 @@ +             +               +      +              +                +                                     +                                     +                                      +                                     +                    +                  +               +               +                                     +                                      +                                     +                                     +                  +                   +                +                    +              +                 +                   +                   diff --git a/src/main/resources/textures/oak_door/topclosed.ans b/src/main/resources/textures/oak_door/topclosed.ans new file mode 100644 index 0000000..02966ff --- /dev/null +++ b/src/main/resources/textures/oak_door/topclosed.ans @@ -0,0 +1,25 @@ +                                             +                                             +                                             +                                              +                                             +                                              +                                              +                                              +                                              +                                              +                                              +                                              +                                             +                                              +                                              +                                              +                                              +                                              +                                              +                                              +                                              +                                             +                                              +                                              +