diff --git a/src/main/java/cz/jzitnik/Main.java b/src/main/java/cz/jzitnik/Main.java index 9bbc5ea..e15e4d2 100644 --- a/src/main/java/cz/jzitnik/Main.java +++ b/src/main/java/cz/jzitnik/Main.java @@ -10,6 +10,9 @@ import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; import java.io.IOException; +import java.util.Optional; + +import static cz.jzitnik.game.ui.InventoryClickHandler.click; public class Main { public static void main(String[] args) { @@ -45,7 +48,7 @@ public class Main { MouseEvent mouseEvent = terminal.readMouseEvent(); switch (game.getWindow()) { case WORLD -> mouseHandler.handle(mouseEvent); - case INVENTORY -> game.getInventory().click(mouseEvent, terminal, screenRenderer, game); + case INVENTORY -> click(mouseEvent, terminal, screenRenderer, game, Optional.empty()); } } } diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index 1f96162..dce2f5b 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -236,7 +236,7 @@ public class Game { } if (!blocks.stream().allMatch(Block::isGhost)) { - return; + RightClickHandler.handle(x, y, this); } if (!(inventory.getItemInHand().isPresent() && inventory.getItemInHand().get().getType() == ItemType.BLOCK)) { diff --git a/src/main/java/cz/jzitnik/game/RightClickHandler.java b/src/main/java/cz/jzitnik/game/RightClickHandler.java new file mode 100644 index 0000000..4ec3cd2 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/RightClickHandler.java @@ -0,0 +1,7 @@ +package cz.jzitnik.game; + +public class RightClickHandler { + public static void handle(int x, int y, Game game) { + + } +} diff --git a/src/main/java/cz/jzitnik/game/SpriteLoader.java b/src/main/java/cz/jzitnik/game/SpriteLoader.java index f7e4fb0..7ef6dd9 100644 --- a/src/main/java/cz/jzitnik/game/SpriteLoader.java +++ b/src/main/java/cz/jzitnik/game/SpriteLoader.java @@ -16,6 +16,7 @@ public class SpriteLoader { STONE, BEDROCK, BREAKING, + CRAFTING_TABLE, OAK_LOG, OAK_LEAF, @@ -24,7 +25,9 @@ public class SpriteLoader { // Items ITEM_DIRT, ITEM_OAK_LOG, - ITEM_OAK_PLANKS + ITEM_OAK_PLANKS, + ITEM_STICK, + ITEM_CRAFTING_TABLE } public static final HashMap SPRITES_MAP = new HashMap<>(); @@ -40,10 +43,13 @@ public class SpriteLoader { SPRITES_MAP.put(SPRITES.OAK_LOG, new OakLog()); SPRITES_MAP.put(SPRITES.OAK_LEAF, new OakLeaf()); SPRITES_MAP.put(SPRITES.OAK_PLANKS, new SimpleSprite("oak_planks.ans")); + SPRITES_MAP.put(SPRITES.CRAFTING_TABLE, new SimpleSprite("crafting_table.ans")); SPRITES_MAP.put(SPRITES.ITEM_DIRT, new SimpleSprite("items/dirt.ans")); SPRITES_MAP.put(SPRITES.ITEM_OAK_LOG, new SimpleSprite("items/oak_log.ans")); SPRITES_MAP.put(SPRITES.ITEM_OAK_PLANKS, new SimpleSprite("items/oak_planks.ans")); + SPRITES_MAP.put(SPRITES.ITEM_STICK, new SimpleSprite("items/stick.ans")); + SPRITES_MAP.put(SPRITES.ITEM_CRAFTING_TABLE, new SimpleSprite("items/crafting_table.ans")); } public static SpriteList load() { diff --git a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipe.java b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipe.java index 2eb113c..f74c4ad 100644 --- a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipe.java +++ b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipe.java @@ -1,6 +1,6 @@ package cz.jzitnik.game.crafting; -import cz.jzitnik.game.items.Item; +import cz.jzitnik.game.items.InventoryItem; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,5 +10,5 @@ import java.util.function.Supplier; @Getter public class CraftingRecipe { private String[][] recipe; - private Supplier itemSupplier; + private Supplier itemSupplier; } diff --git a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java index 29f0988..ace2df7 100644 --- a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java +++ b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java @@ -1,18 +1,133 @@ package cz.jzitnik.game.crafting; +import cz.jzitnik.game.items.InventoryItem; import cz.jzitnik.game.items.ItemBlockSupplier; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class CraftingRecipeList { private static List recipes = new ArrayList<>(); static { - String[][] pattern = new String[9][9]; - for (int i = 0; i < 9; i++) { - pattern[i][i] = "oak_log"; + // Oak planks + recipes.add(new CraftingRecipe(new String[][]{ + {"oak_log", null, null}, + {null, null, null}, + {null, null, null} + }, () -> new InventoryItem(4, ItemBlockSupplier.Items.oakPlanks()))); + + recipes.add(new CraftingRecipe(new String[][]{ + {"oak_planks", "oak_planks", null}, + {"oak_planks", "oak_planks", null}, + {null, null, null} + }, () -> new InventoryItem(1, ItemBlockSupplier.Items.craftingTable()))); + + recipes.add(new CraftingRecipe(new String[][]{ + {"oak_planks", null, null}, + {"oak_planks", null, null}, + {null, null, null} + }, () -> new InventoryItem(4, ItemBlockSupplier.Items.stick()))); + } + + // 2x2 crafting + public static Optional getRecipeSmall(String[] r) { + for (CraftingRecipe recipe : recipes) { + if (matchesByItemSet(recipe.getRecipe(), r)) { + return Optional.of(recipe); + } } - recipes.add(new CraftingRecipe(pattern, ItemBlockSupplier.Items::oakPlanks)); + return Optional.empty(); + } + + // 3x3 crafting + public static Optional getRecipeFull(String[] r) { + for (CraftingRecipe recipe : recipes) { + if (matchesByItemSet(recipe.getRecipe(), r)) { + return Optional.of(recipe); + } + } + return Optional.empty(); + } + + public static String[][] trimNullEdges(String[][] array) { + int n = array.length; + int minRow = n, maxRow = -1, minCol = n, maxCol = -1; + + // Find the bounding box of non-null values + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (array[i][j] != null) { + minRow = Math.min(minRow, i); + maxRow = Math.max(maxRow, i); + minCol = Math.min(minCol, j); + maxCol = Math.max(maxCol, j); + } + } + } + + // If all elements are null, return an empty array + if (maxRow == -1) { + return new String[0][0]; + } + + int newSizeRow = maxRow - minRow + 1; + int newSizeCol = maxCol - minCol + 1; + String[][] trimmedArray = new String[newSizeRow][newSizeCol]; + + // Copy the relevant portion of the array + for (int i = 0; i < newSizeRow; i++) { + System.arraycopy(array[minRow + i], minCol, trimmedArray[i], 0, newSizeCol); + } + + return trimmedArray; + } + + public static boolean are2DArraysIdentical(String[][] array1, String[][] array2) { + if (array1.length != array2.length) { + return false; + } + + for (int i = 0; i < array1.length; i++) { + if (array1[i].length != array2[i].length) { + return false; + } + + for (int j = 0; j < array1[i].length; j++) { + if (array1[i][j] == null && array2[i][j] != null) { + return false; + } + if (array1[i][j] != null && !array1[i][j].equals(array2[i][j])) { + return false; + } + } + } + + return true; + } + + private static boolean matchesByItemSet(String[][] recipe, String[] grid) { + String[][] finalGrid = new String[3][3]; + + if (grid.length == 4) { + finalGrid[0][0] = grid[0]; + finalGrid[0][1] = grid[1]; + finalGrid[1][0] = grid[2]; + finalGrid[1][1] = grid[3]; + } else { + finalGrid[0][0] = grid[0]; + finalGrid[0][1] = grid[1]; + finalGrid[0][2] = grid[2]; + finalGrid[1][0] = grid[3]; + finalGrid[1][1] = grid[4]; + finalGrid[1][2] = grid[5]; + finalGrid[2][0] = grid[6]; + finalGrid[2][1] = grid[7]; + finalGrid[2][2] = grid[8]; + } + + var croppedRecipe = trimNullEdges(recipe); + var croppedGrid = trimNullEdges(finalGrid); + + return are2DArraysIdentical(croppedRecipe, croppedGrid); } } diff --git a/src/main/java/cz/jzitnik/game/items/Item.java b/src/main/java/cz/jzitnik/game/items/Item.java index 072e4f7..267cc64 100644 --- a/src/main/java/cz/jzitnik/game/items/Item.java +++ b/src/main/java/cz/jzitnik/game/items/Item.java @@ -29,6 +29,14 @@ public class Item { this.block = Optional.of(block); } + public Item(String id, String name, ItemType type, SpriteLoader.SPRITES sprite) { + this.id = id; + this.name = name; + this.type = type; + this.sprite = sprite; + this.block = Optional.empty(); + } + public void use() { durability--; } diff --git a/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java b/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java index 18f06e4..0fa07ef 100644 --- a/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java +++ b/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java @@ -20,7 +20,10 @@ public class ItemBlockSupplier { return new Item("oak_log", "Oak log", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_OAK_LOG, ref); } public static Item oakPlanks(Block ref) { - return new Item("oak_log", "Oak planks", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_OAK_PLANKS, ref); + return new Item("oak_planks", "Oak planks", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_OAK_PLANKS, ref); + } + public static Item craftingTable(Block ref) { + return new Item("crafting_table", "Crafting table", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_CRAFTING_TABLE, ref); } } @@ -50,6 +53,11 @@ public class ItemBlockSupplier { block.setDrops(List.of(Helper.oakPlanks(block))); return block; } + public static Block craftingTable() { + var block = new Block("crafting_table", SpriteLoader.SPRITES.CRAFTING_TABLE, 3, ItemType.AXE, new ArrayList<>()); + block.setDrops(List.of(Helper.craftingTable(block))); + return block; + } } // I hate this but whatever @@ -60,5 +68,11 @@ public class ItemBlockSupplier { public static Item oakPlanks() { return Helper.oakPlanks(Blocks.oakPlanks()); } + public static Item stick() { + return new Item("stick", "Stick", ItemType.USELESS_ITEM, SpriteLoader.SPRITES.ITEM_STICK); + } + public static Item craftingTable() { + return Helper.craftingTable(Blocks.craftingTable()); + } } } diff --git a/src/main/java/cz/jzitnik/game/items/ItemType.java b/src/main/java/cz/jzitnik/game/items/ItemType.java index d604e29..007de75 100644 --- a/src/main/java/cz/jzitnik/game/items/ItemType.java +++ b/src/main/java/cz/jzitnik/game/items/ItemType.java @@ -5,5 +5,6 @@ public enum ItemType { SHOVEL, AXE, SHEARS, - BLOCK + BLOCK, + USELESS_ITEM } diff --git a/src/main/java/cz/jzitnik/game/ui/Inventory.java b/src/main/java/cz/jzitnik/game/ui/Inventory.java index 9e1a37a..94a6957 100644 --- a/src/main/java/cz/jzitnik/game/ui/Inventory.java +++ b/src/main/java/cz/jzitnik/game/ui/Inventory.java @@ -1,15 +1,12 @@ package cz.jzitnik.game.ui; -import cz.jzitnik.game.Game; import cz.jzitnik.game.items.InventoryItem; import cz.jzitnik.game.items.Item; -import cz.jzitnik.tui.ScreenRenderer; import cz.jzitnik.tui.SpriteCombiner; -import cz.jzitnik.game.sprites.ui.Number; import cz.jzitnik.tui.SpriteList; +import cz.jzitnik.utils.Numbers; import lombok.Getter; import lombok.Setter; -import org.jline.terminal.MouseEvent; import org.jline.terminal.Terminal; import java.util.ArrayList; @@ -18,9 +15,9 @@ import java.util.Optional; @Getter public class Inventory { - private static final int INVENTORY_SIZE_PX = 470; - private static int COLUMN_AMOUNT = 5; - private static int ROW_AMOUNT = 4; + public static final int INVENTORY_SIZE_PX = 470; + public static final int COLUMN_AMOUNT = 5; + public static final int ROW_AMOUNT = 4; private InventoryItem[] items = new InventoryItem[20]; private InventoryItem[] hotbar = new InventoryItem[9]; @@ -30,6 +27,8 @@ public class Inventory { private int itemInhHandIndex = 0; @Setter private int selectedItemInv = -1; + @Setter + private boolean rightClick = false; public Optional getItemInHand() { if (hotbar[itemInhHandIndex] == null) { @@ -73,6 +72,12 @@ public class Inventory { // If inventory is full the item is lost } + public void addItem(InventoryItem item) { + for (int i = 0; i < item.getAmount(); i++) { + addItem(item.getItem()); + } + } + public void addItem(Item item) { if (!item.isStackable()) { placeItem(item); @@ -99,42 +104,6 @@ public class Inventory { placeItem(item); } - private Number.NumberState getNumberState(int digit) { - return switch (digit) { - case 0 -> Number.NumberState.ZERO; - case 1 -> Number.NumberState.ONE; - case 2 -> Number.NumberState.TWO; - case 3 -> Number.NumberState.THREE; - case 4 -> Number.NumberState.FOUR; - case 5 -> Number.NumberState.FIVE; - case 6 -> Number.NumberState.SIX; - case 7 -> Number.NumberState.SEVEN; - case 8 -> Number.NumberState.EIGHT; - case 9 -> Number.NumberState.NINE; - default -> throw new IllegalArgumentException("Unexpected number: " + digit); - }; - } - - private String getNumberSprite(int number) { - Number nm = new Number(); - if (number <= 9) { - Number.NumberState numberState = getNumberState(number); - - return Number.fixForHotbar(nm.getSprite(numberState), 1); - } - - String numStr = Integer.toString(number); // Convert to string - char firstChar = numStr.charAt(0); // Get first digit - char secondChar = numStr.charAt(1); // Get second digit - - int firstDigit = Character.getNumericValue(firstChar); - int secondDigit = Character.getNumericValue(secondChar); - - Number.NumberState numberState1 = getNumberState(firstDigit); - Number.NumberState numberState2 = getNumberState(secondDigit); - - return SpriteCombiner.combineTwoSprites( Number.fixForHotbar(nm.getSprite(numberState1), 2), Number.fixForHotbar(nm.getSprite(numberState2), 1)); - } private String getHotbarBackground() { StringBuilder sprite = new StringBuilder(); @@ -174,12 +143,12 @@ public class Inventory { } } - public void renderFull(StringBuilder buffer, Terminal terminal, SpriteList spriteList) { + public void renderFull(StringBuilder buffer, Terminal terminal, SpriteList spriteList, boolean includeCrafting, Optional moveTopCustom) { int widthPixels = COLUMN_AMOUNT * (50 + 4) + 2; int heightPixels = ROW_AMOUNT * (25 + 1); int moveLeft = (terminal.getWidth() / 2) - (widthPixels / 2); - int moveTop = (terminal.getHeight() / 2) - (heightPixels / 2); + int moveTop = moveTopCustom.orElse((terminal.getHeight() / 2) - (heightPixels / 2)); List sprites = getSprites(items, spriteList, selectedItemInv); @@ -218,7 +187,7 @@ public class Inventory { } } - if (i + 1 > spacesFromTop) { + if (i + 1 > spacesFromTop && includeCrafting) { int craftingIndex = ((i - spacesFromTop) * 26) + j; if (craftingIndex >= 0 && craftingIndex < craftingTable.length) { @@ -236,69 +205,6 @@ public class Inventory { renderHotbar(buffer, spriteList, terminal, true); } - public void click(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer, Game game) { - if (mouseEvent.getType() != MouseEvent.Type.Pressed) { - return; - } - - int x = mouseEvent.getX(); - int y = mouseEvent.getY(); - - int widthPixels = COLUMN_AMOUNT * (50 + 4) + 2; - int heightPixels = ROW_AMOUNT * (25 + 1); - - int moveLeft = (terminal.getWidth() / 2) - (widthPixels / 2); - int moveTop = (terminal.getHeight() / 2) - (heightPixels / 2); - - int fx = x - moveLeft; - int fy = y - moveTop; - - int startLeftHotbar = (terminal.getWidth() / 2) - (INVENTORY_SIZE_PX / 2); - int startTopHotbar = moveTop + heightPixels + 10; - - int startLeftCrafting = moveLeft + widthPixels + 20; - int startTopCrafting = moveTop + 26; - - if (x >= startLeftCrafting && y >= startTopCrafting && y <= startTopCrafting + 52 && x <= startLeftCrafting + 106 + 54 + 7) { - smallCraftingTable.click(mouseEvent.getX() - startLeftCrafting, mouseEvent.getY() - startTopCrafting); - screenRenderer.render(game); - return; - } - - if (x >= startLeftHotbar && y >= startTopHotbar && x <= startLeftHotbar + INVENTORY_SIZE_PX && y <= startTopHotbar + 26) { - int index = (x - startLeftHotbar) / 51; - if (hotbar[index] == null && selectedItemInv != -1) { - hotbar[index] = getSelectedItem(); - } else if (hotbar[index] != null) { - selectedItemInv = 20 + index; - } - - screenRenderer.render(game); - return; - } - - if (!(fx >= 0 && fx <= widthPixels && fy >=0 && fy <= heightPixels)) { - return; - } - - switch (mouseEvent.getButton()) { - case Button1 -> { - int blockX = fx / 54; - int blockY = fy / 26; - - InventoryItem inventoryItem = items[blockY * COLUMN_AMOUNT + blockX]; - if (inventoryItem != null) { - selectedItemInv = blockY * COLUMN_AMOUNT + blockX; - } - if (inventoryItem == null && selectedItemInv != -1) { - items[blockY * COLUMN_AMOUNT + blockX] = getSelectedItem(); - } - } - } - - screenRenderer.render(game); - } - public List getSprites(InventoryItem[] items, SpriteList spriteList, int selectedItem) { List sprites = new ArrayList<>(); @@ -318,37 +224,127 @@ public class Inventory { if (i == selectedItem) { sprites.add(SpriteCombiner.combineTwoSprites(getHotbarBackground(), SpriteCombiner.combineTwoSprites( spriteList.getSprite(item.getItem().getSprite()).getSprite(), - getNumberSprite(item.getAmount()) + Numbers.getNumberSprite(item.getAmount()) ))); continue; } sprites.add(SpriteCombiner.combineTwoSprites( spriteList.getSprite(item.getItem().getSprite()).getSprite(), - getNumberSprite(item.getAmount()) + Numbers.getNumberSprite(item.getAmount()) )); } return sprites; } + public InventoryDTO getItem(int index) { + InventoryItem temp; + if (index < 20) { + // Normal inventory + return new InventoryDTO(items, index); + } else if (index >= 29) { + // Small crafting table + return new InventoryDTO(smallCraftingTable.getItems(), index - 29); + } else { + // Hotbar + return new InventoryDTO(hotbar, index - 20); + } + } + public InventoryItem getSelectedItem() { InventoryItem temp; - if (selectedItemInv < 20) { - temp = items[selectedItemInv]; - items[selectedItemInv] = null; - } else if (selectedItemInv >= 29) { - int index = selectedItemInv - 29; - temp = smallCraftingTable.getItems()[index]; - smallCraftingTable.getItems()[index] = null; - } else { - int index = selectedItemInv - 20; - temp = hotbar[index]; - hotbar[index] = null; - } + InventoryDTO data = getItem(selectedItemInv); + temp = data.getObj()[data.getIndex()]; + data.getObj()[data.getIndex()] = null; selectedItemInv = -1; + rightClick = false; return temp; } + + public InventoryItem getOne() { + InventoryDTO inventoryItem = getItem(selectedItemInv); + InventoryItem item = inventoryItem.getObj()[inventoryItem.getIndex()]; + if (item.getAmount() == 1) { + return getSelectedItem(); + } + + item.setAmount(item.getAmount() - 1); + + rightClick = false; + + return new InventoryItem(1, item.getItem()); + } + + public InventoryItem getHalf() { + InventoryDTO inventoryItem = getItem(selectedItemInv); + InventoryItem item = inventoryItem.getObj()[inventoryItem.getIndex()]; + if (item.getAmount() == 1) { + return getSelectedItem(); + } + + int half = item.getAmount() / 2; + item.setAmount(item.getAmount() - half); + + selectedItemInv = -1; + rightClick = false; + + return new InventoryItem(half, item.getItem()); + } + + public boolean hasSelectedItem() { + return selectedItemInv != -1; + } + + public void mergeItems(int indexFrom, int indexTo) { + InventoryDTO fromData = getItem(indexFrom); + InventoryDTO toData = getItem(indexTo); + + InventoryItem fromItem = fromData.getObj()[fromData.getIndex()]; + InventoryItem toItem = toData.getObj()[toData.getIndex()]; + + if (fromItem == null || toItem == null) { + return; // Nothing to merge + } + + if (!fromItem.getItem().equals(toItem.getItem())) { + return; // Different items cannot be merged + } + + int totalAmount = fromItem.getAmount() + toItem.getAmount(); + + if (totalAmount > 64) { + toItem.setAmount(64); + fromItem.setAmount(totalAmount - 64); + } else { + toItem.setAmount(totalAmount); + fromData.getObj()[fromData.getIndex()] = null; // Remove the source item after merging + } + + selectedItemInv = -1; + } + + public void mergeOne(int fromIndex, int toIndex) { + InventoryDTO fromSlot = getItem(fromIndex); + InventoryDTO toSlot = getItem(toIndex); + + InventoryItem fromItem = fromSlot.getObj()[fromSlot.getIndex()]; + InventoryItem toItem = toSlot.getObj()[toSlot.getIndex()]; + + if (fromItem == null || !fromItem.getItem().equals(toItem.getItem()) || toItem.getAmount() >= fromItem.getItem().getStackAmount()) { + return; // Can't merge + } + + // Move only one item + toItem.setAmount(toItem.getAmount() + 1); + fromItem.setAmount(fromItem.getAmount() - 1); + + // Remove source item if empty + if (fromItem.getAmount() <= 0) { + fromSlot.getObj()[fromSlot.getIndex()] = null; + selectedItemInv = -1; + } + } } diff --git a/src/main/java/cz/jzitnik/game/ui/InventoryClickHandler.java b/src/main/java/cz/jzitnik/game/ui/InventoryClickHandler.java new file mode 100644 index 0000000..766a243 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/ui/InventoryClickHandler.java @@ -0,0 +1,120 @@ +package cz.jzitnik.game.ui; + +import cz.jzitnik.game.Game; +import cz.jzitnik.tui.ScreenRenderer; +import org.jline.terminal.MouseEvent; +import org.jline.terminal.Terminal; + +import java.util.Optional; + +import static cz.jzitnik.game.ui.Inventory.*; + +public class InventoryClickHandler { + public static void click(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer, Game game, Optional moveTopCustom) { + if (mouseEvent.getType() != MouseEvent.Type.Pressed) return; + + var inventory = game.getInventory(); + int x = mouseEvent.getX(); + int y = mouseEvent.getY(); + + int moveLeft = calculateMoveLeft(terminal); + int moveTop = moveTopCustom.orElse(calculateMoveTop(terminal)); + + if (handleCraftingTableClick(mouseEvent, x, y, inventory, screenRenderer, game, moveLeft, moveTop)) return; + if (handleHotbarClick(mouseEvent, x, y, inventory, screenRenderer, game, terminal, moveTop)) return; + handleInventoryClick(mouseEvent, x, y, inventory, screenRenderer, game, moveLeft, moveTop); + } + + private static int calculateMoveLeft(Terminal terminal) { + return (terminal.getWidth() / 2) - ((COLUMN_AMOUNT * (50 + 4) + 2) / 2); + } + + private static int calculateMoveTop(Terminal terminal) { + return (terminal.getHeight() / 2) - ((ROW_AMOUNT * (25 + 1)) / 2); + } + + private static boolean handleCraftingTableClick(MouseEvent mouseEvent, int x, int y, Inventory inventory, ScreenRenderer screenRenderer, Game game, int moveLeft, int moveTop) { + int startLeftCrafting = moveLeft + (COLUMN_AMOUNT * (50 + 4) + 2) + 20; + int startTopCrafting = moveTop + 26; + + if (x < startLeftCrafting || y < startTopCrafting || x > startLeftCrafting + 167 || y > startTopCrafting + 52) return false; + + int craftX = x - startLeftCrafting; + int craftY = y - startTopCrafting; + + if (craftX > 106 && craftY > 10 && craftY < 37) { + inventory.getSmallCraftingTable().pickup(); + screenRenderer.render(game); + return true; + } + + int blockIndex = (craftY / 26) * 2 + (craftX / 53); + handleItemClick(mouseEvent, inventory, inventory.getSmallCraftingTable().getItems(), blockIndex, 29); + screenRenderer.render(game); + return true; + } + + private static boolean handleHotbarClick(MouseEvent mouseEvent, int x, int y, Inventory inventory, ScreenRenderer screenRenderer, Game game, Terminal terminal, int moveTop) { + int startLeftHotbar = (terminal.getWidth() / 2) - (INVENTORY_SIZE_PX / 2) + 2; + int startTopHotbar = moveTop + (ROW_AMOUNT * (25 + 1)) + 10; + + if (x < startLeftHotbar || y < startTopHotbar || x > startLeftHotbar + INVENTORY_SIZE_PX || y > startTopHotbar + 26) return false; + + int index = (x - startLeftHotbar) / 52; + handleItemClick(mouseEvent, inventory, inventory.getHotbar(), index, 20); + screenRenderer.render(game); + return true; + } + + private static void handleInventoryClick(MouseEvent mouseEvent, int x, int y, Inventory inventory, ScreenRenderer screenRenderer, Game game, int moveLeft, int moveTop) { + int fx = x - moveLeft; + int fy = y - moveTop; + + int widthPixels = COLUMN_AMOUNT * (50 + 4) + 2; + int heightPixels = ROW_AMOUNT * (25 + 1); + + if (fx < 0 || fx > widthPixels || fy < 0 || fy > heightPixels) return; + + int blockIndex = (fy / 26) * COLUMN_AMOUNT + (fx / 54); + handleItemClick(mouseEvent, inventory, inventory.getItems(), blockIndex, 0); + screenRenderer.render(game); + } + + private static void handleItemClick(MouseEvent mouseEvent, Inventory inventory, Object[] items, int index, int offset) { + int actualIndex = index + offset; + + if (inventory.getSelectedItemInv() == actualIndex) { + return; + } + + switch (mouseEvent.getButton()) { + case Button1 -> { + if (items[index] == null && inventory.hasSelectedItem()) { + items[index] = inventory.isRightClick() ? inventory.getHalf() : inventory.getSelectedItem(); + } else if (items[index] != null) { + if (inventory.hasSelectedItem()) { + // Merge items + inventory.mergeItems(inventory.getSelectedItemInv(), actualIndex); + } else { + inventory.setSelectedItemInv(index + offset); + } + } + } + case Button3 -> { + if (items[index] != null) { + if (inventory.hasSelectedItem()) { + // Merge just one item into the stack + inventory.mergeOne(inventory.getSelectedItemInv(), actualIndex); + } else { + // Pick up one item + inventory.setSelectedItemInv(actualIndex); + inventory.setRightClick(true); + } + } else { + // Place one item + items[index] = inventory.getOne(); + } + } + } + } +} diff --git a/src/main/java/cz/jzitnik/game/ui/InventoryDTO.java b/src/main/java/cz/jzitnik/game/ui/InventoryDTO.java new file mode 100644 index 0000000..ca0a335 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/ui/InventoryDTO.java @@ -0,0 +1,12 @@ +package cz.jzitnik.game.ui; + +import cz.jzitnik.game.items.InventoryItem; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class InventoryDTO { + private InventoryItem[] obj; + private int index; +} diff --git a/src/main/java/cz/jzitnik/game/ui/SmallCraftingTable.java b/src/main/java/cz/jzitnik/game/ui/SmallCraftingTable.java index e57c672..047a3f4 100644 --- a/src/main/java/cz/jzitnik/game/ui/SmallCraftingTable.java +++ b/src/main/java/cz/jzitnik/game/ui/SmallCraftingTable.java @@ -1,11 +1,14 @@ package cz.jzitnik.game.ui; +import cz.jzitnik.game.crafting.CraftingRecipe; +import cz.jzitnik.game.crafting.CraftingRecipeList; import cz.jzitnik.game.items.InventoryItem; -import cz.jzitnik.game.items.Item; -import cz.jzitnik.game.items.ItemBlockSupplier; +import cz.jzitnik.tui.SpriteCombiner; import cz.jzitnik.tui.SpriteList; +import cz.jzitnik.utils.Numbers; import lombok.Getter; +import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -17,26 +20,25 @@ public class SmallCraftingTable { @Getter private InventoryItem[] items = new InventoryItem[4]; - public void click(int x, int y) { - if (x > 106) { - if (y > 10 && y < 37) { - // Pickup item from crafting table + public void pickup() { + Optional recipe = CraftingRecipeList.getRecipeSmall(Arrays.stream(items).map(item -> item == null ? null: item.getItem().getId()).toArray(String[]::new)); - return; + if (recipe.isPresent()) { + InventoryItem item = recipe.get().getItemSupplier().get(); + inventory.addItem(item); + + for (int i = 0; i < items.length; i++) { + InventoryItem it = items[i]; + if (it == null) { + continue; + } + + if (it.getAmount() == 1) { + items[i] = null; + } else { + it.setAmount(it.getAmount() - 1); + } } - - return; - } - - int blockX = x / 53; - int blockY = y / 26; - - int blockIndex = blockY * COLUMN_AMOUNT + blockX; - - if (items[blockIndex] == null && inventory.getSelectedItemInv() != -1) { - items[blockIndex] = inventory.getSelectedItem(); - } else if (items[blockIndex] != null) { - inventory.setSelectedItemInv(blockIndex + 29); } } @@ -45,9 +47,14 @@ public class SmallCraftingTable { List sprites = inventory.getSprites(items, spriteList, inventory.getSelectedItemInv() - 29); - Optional craftedItem = Optional.of(ItemBlockSupplier.Items.dirt()); + Optional recipe = CraftingRecipeList.getRecipeSmall(Arrays.stream(items).map(item -> item == null ? null: item.getItem().getId()).toArray(String[]::new)); - String[] craftedSprite = craftedItem.isPresent() ? spriteList.getSprite(craftedItem.get().getSprite()).getSprite().split("\n") : null; + Optional craftedItem = recipe.map(craftingRecipe -> craftingRecipe.getItemSupplier().get()); + + String[] craftedSprite = craftedItem.map(inventoryItem -> SpriteCombiner.combineTwoSprites( + spriteList.getSprite(inventoryItem.getItem().getSprite()).getSprite(), + Numbers.getNumberSprite(inventoryItem.getAmount()) + ).split("\n")).orElse(null); int counter = 0; for (int i = 0; i < ROW_AMOUNT; i++) { diff --git a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java index 20205ff..ac48968 100644 --- a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java +++ b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java @@ -42,7 +42,7 @@ public class ScreenRenderer { main.append("\033[H\033[2J"); switch (game.getWindow()) { - case INVENTORY -> game.getInventory().renderFull(main, terminal, spriteList); + case INVENTORY -> game.getInventory().renderFull(main, terminal, spriteList, true, Optional.empty()); case WORLD -> { // World diff --git a/src/main/java/cz/jzitnik/utils/Numbers.java b/src/main/java/cz/jzitnik/utils/Numbers.java new file mode 100644 index 0000000..c50e6e5 --- /dev/null +++ b/src/main/java/cz/jzitnik/utils/Numbers.java @@ -0,0 +1,52 @@ +package cz.jzitnik.utils; + +import cz.jzitnik.game.sprites.ui.Number; +import cz.jzitnik.tui.SpriteCombiner; + +public class Numbers { + private static Number.NumberState getNumberState(int digit) { + return switch (digit) { + case 0 -> Number.NumberState.ZERO; + case 1 -> Number.NumberState.ONE; + case 2 -> Number.NumberState.TWO; + case 3 -> Number.NumberState.THREE; + case 4 -> Number.NumberState.FOUR; + case 5 -> Number.NumberState.FIVE; + case 6 -> Number.NumberState.SIX; + case 7 -> Number.NumberState.SEVEN; + case 8 -> Number.NumberState.EIGHT; + case 9 -> Number.NumberState.NINE; + default -> throw new IllegalArgumentException("Unexpected number: " + digit); + }; + } + + public static String getNumberSprite(int number) { + if (number == 1) { + StringBuilder sprite = new StringBuilder(); + for (int i = 0; i < 25; i++) { + sprite.append("\033[0m ".repeat(50)); + sprite.append("\n"); + } + return sprite.toString(); + } + + Number nm = new Number(); + if (number <= 9) { + Number.NumberState numberState = getNumberState(number); + + return Number.fixForHotbar(nm.getSprite(numberState), 1); + } + + String numStr = Integer.toString(number); // Convert to string + char firstChar = numStr.charAt(0); // Get first digit + char secondChar = numStr.charAt(1); // Get second digit + + int firstDigit = Character.getNumericValue(firstChar); + int secondDigit = Character.getNumericValue(secondChar); + + Number.NumberState numberState1 = getNumberState(firstDigit); + Number.NumberState numberState2 = getNumberState(secondDigit); + + return SpriteCombiner.combineTwoSprites(Number.fixForHotbar(nm.getSprite(numberState1), 2), Number.fixForHotbar(nm.getSprite(numberState2), 1)); + } +} diff --git a/src/main/resources/textures/crafting_table.ans b/src/main/resources/textures/crafting_table.ans new file mode 100644 index 0000000..cd404e2 --- /dev/null +++ b/src/main/resources/textures/crafting_table.ans @@ -0,0 +1,26 @@ +   ▒▒▒▒▒▒▒▒▒▒▒▒▒   ░░░░░░░░░░░░░   ▒▒▒▒▒▒▒▒▒▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒   ░░░░░░░░░░░░░   ▒▒▒▒▒▒▒▒▒▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒   ░░░░░░   ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒         ▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒         ▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▒▒▒   ▒▒▒   ▒▒▒    +   ▒▒▒▒▒▒▒   ▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒         ▒▒▒    +   ▒▒▒▒▒▒▒   ▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒         ▒▒▒    +   ▒▒▒▒▒▒▒   ▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▒▒▒    +   ░▒▒▒▒▒▒   ▒▒▒▒▒▒▒▒▒      ░▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▒▒▒    +   ░▒▒▒▒▒▒   ▒▒▒▒▒▒▒▒▒      ░▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▒▒▒    +   ▒▒▒▒▒▒▒   ▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▒▒▒    +   ▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒    +   ▒▒▒▒▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒    +   ▒▒▒▒▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒    +   ▒▒▒▒▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒    +   ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    + \ No newline at end of file diff --git a/src/main/resources/textures/items/crafting_table.ans b/src/main/resources/textures/items/crafting_table.ans new file mode 100644 index 0000000..c38011a --- /dev/null +++ b/src/main/resources/textures/items/crafting_table.ans @@ -0,0 +1,26 @@ +                                                   +                      ▓▓▒▒▓▓▓                      +                  ░ ░░ ▒▒ ▒▒ ░  ░                  +              ░░▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒░░░              +           ░▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒░▒▒▒▒▒░░           +      ▓▓▒  ▒▒▒▒░░░░░▒▒▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒  ▓▓▓      +   ▓▓  ▒▒▒▒▒▒▒▒▒▒▒░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓   +   ▓▓▒▒▒▒  ░▒▒▒▒▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒   ▒▒▒▒▓▓   +   ▓▓▒▒▒▒▒ ░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░   ▒▒▒▒▒▓▓   +   ▓▓▒▒▒▒▒▒     ░░░▒░░▒▒▒▒▒▒▒░░░░░     ▒▒▒▒▒░░▓▓   +   ▓▓▒▒▒▒▒▒▒▒     ▒▒▒ ▓▒▒▒▒▓▓ ▒▒      ▒▒░░░░▒▒▓▓   +   ▓▓▒▒ ▒▒▒▒▒▓  ▒▒▒▒▒▒▒▒▓ ▓▒▒▒▒▒▒▒▒ ▓▓░░▒▒▒▒▒▒▓▓   +   ▓▓▒▒  ▒▒▒▒  ▓▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒░░░▓  ▒▒▒▒▒▒▒▒▓▓   +   ▓▓▒▒  ▒▒▒▒   ▒▒    ▒▒▓▓▓▒░░▒▒▒▒▒   ▒▒▒▒░░▒░▓▓   +   ▓▓▒▒  ▒▒▒▒   ▒▒  ▒  ▒▓▓▓░▒▒▒▒  ▒   ░░░░▒▒▒▒▓▓   +   ▓▓▒▓▓▓▓▒▒▒▓▓▓▒▒▓▓   ▒▓▓▓▒▒▒ ▒  ░▓▓▓▒▒▒▒▒▒▒▒▓▓   +   ▓▓▒▒▓▓▓▒▒▒   ▒▒▓▓▓▓▓▒▓▓▓▒▒▒░▓░░▒   ▒▒▒▒▒▒░░▓▓   +   ▓▓▒▒▒▒▒▒▒▒   ▒▒▓▓▓▓▓▒▓▓▓░░░▓▒▓▓▒   ▒▒▒░░▒▒▒▓▓   +   ▓▓▒▒▒▒▒▒▒▒▓▓ ▒▒▒▒▓▓▓▒▓▓▓▒▒▒▓▒▒▒▒ ▓▓░▒▒▒▒▒▒▒▓▓   +   ▓▓▒▒▒▒▒▒▒▒   ▒▒▒▒▓▓▓▒▓▓▓▒▒▒▒▒░░░   ▒▒▒▒▒▒▒░▓▓   +      ▒▒▒▒▒▒▒   ▒▒▒▒▒▓▓▒▓▓▓▒░░▒▒▒▒▒   ▒▒▒░░░░      +          ▒▒▒   ▒▒▒▒▒▓▓▒▓▓▓▒▒▒▒▒▒▒▒  ▓░░░          +              ▓▓▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒░▒▓▓              +                  ▒▒▒▒▒▒▓▓▓▒▒░░░░                  +                      ▒▒▓▓▓░░                      + \ No newline at end of file diff --git a/src/main/resources/textures/items/stick.ans b/src/main/resources/textures/items/stick.ans new file mode 100644 index 0000000..7817d25 --- /dev/null +++ b/src/main/resources/textures/items/stick.ans @@ -0,0 +1,26 @@ +                                                   +                                                   +                                                   +                                         ░░░░░░    +                                                   +                                         ░░░       +                                         ░░░       +                                      ░░░          +                               ░   ░░░             +                               ░   ░░░             +                            ▓  ░░░░                +                            ░░░░                   +                            ░░░░                   +                         ░░░   ▒                   +                   ▓▓▓░░░    ░░░                   +                      ░░░   ░                      +                   ░░░                             +                   ░░░                             +                ░░░                                +             ░░░                                   +             ░░░                                   +      ░   ░░░                                      +      ░                                            +      ░                                            +                                                   + \ No newline at end of file