diff --git a/src/main/java/cz/jzitnik/Main.java b/src/main/java/cz/jzitnik/Main.java index 1f3bde6..4de85d8 100644 --- a/src/main/java/cz/jzitnik/Main.java +++ b/src/main/java/cz/jzitnik/Main.java @@ -50,15 +50,15 @@ public class Main { switch (key) { case 'a': game.movePlayerLeft(screenRenderer); - screenRenderer.render(game.getWorld()); + screenRenderer.render(game); break; case 'd': game.movePlayerRight(screenRenderer); - screenRenderer.render(game.getWorld()); + screenRenderer.render(game); break; case ' ': game.movePlayerUp(screenRenderer); - screenRenderer.render(game.getWorld()); + screenRenderer.render(game); break; case 'q': System.out.println("Exiting game..."); @@ -77,7 +77,7 @@ public class Main { // Game loop (rendering the game) while (isRunning[0]) { - screenRenderer.render(game.getWorld()); + screenRenderer.render(game); try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/src/main/java/cz/jzitnik/game/Block.java b/src/main/java/cz/jzitnik/game/Block.java index 708bd11..7de2428 100644 --- a/src/main/java/cz/jzitnik/game/Block.java +++ b/src/main/java/cz/jzitnik/game/Block.java @@ -1,8 +1,12 @@ package cz.jzitnik.game; +import cz.jzitnik.game.items.Item; import cz.jzitnik.game.items.ItemType; +import cz.jzitnik.game.items.ToolVariant; import lombok.Getter; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; @Getter @@ -11,31 +15,39 @@ public class Block { private SpriteLoader.SPRITES sprite; private Optional spriteState = Optional.empty(); private boolean ghost = false; + private boolean isMineable = true; private int hardness = 1; private Optional tool = Optional.empty(); + private List toolVariants = new ArrayList<>(); + private List drops = new ArrayList<>(); public Block(String blockId, SpriteLoader.SPRITES sprite) { this.blockId = blockId; this.sprite = sprite; } - public Block(String blockId, SpriteLoader.SPRITES sprite, boolean ghost) { + public Block(String blockId, SpriteLoader.SPRITES sprite, boolean ghost, boolean isMineable) { this.blockId = blockId; this.sprite = sprite; this.ghost = ghost; + this.isMineable = isMineable; } - public Block(String blockId, SpriteLoader.SPRITES sprite, int hardness) { - this.blockId = blockId; - this.sprite = sprite; - this.hardness = hardness; - } - - public Block(String blockId, SpriteLoader.SPRITES sprite, int hardness, ItemType tool) { + public Block(String blockId, SpriteLoader.SPRITES sprite, int hardness, ItemType tool, List toolVariants) { this.blockId = blockId; this.sprite = sprite; this.hardness = hardness; this.tool = Optional.of(tool); + this.toolVariants = toolVariants; + } + + public Block(String blockId, SpriteLoader.SPRITES sprite, int hardness, ItemType tool, List toolVariants, List drops) { + this.blockId = blockId; + this.sprite = sprite; + this.hardness = hardness; + this.tool = Optional.of(tool); + this.toolVariants = toolVariants; + this.drops = drops; } public void setSpriteState(Enum spriteState) { diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index 5b3dced..b8210f8 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -1,87 +1,29 @@ package cz.jzitnik.game; import cz.jzitnik.game.items.Item; -import cz.jzitnik.game.items.ItemType; import cz.jzitnik.game.sprites.Breaking; import cz.jzitnik.tui.ScreenMovingCalculationProvider; import cz.jzitnik.tui.ScreenRenderer; import lombok.Getter; +import lombok.Setter; import org.jline.terminal.Terminal; import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.Random; @Getter public class Game { private List[][] world = new ArrayList[256][512]; + @Setter private Block player; private boolean mining = false; - private List inventory; + private Inventory inventory = new Inventory(); private Optional itemInHand = Optional.empty(); public Game() { - for (int i = 0; i < 256; i++) { - for (int j = 0; j < 512; j++) { - world[i][j] = new ArrayList<>(); - } - } - - Block steveBlock = new Block("steve", SpriteLoader.SPRITES.STEVE); - player = steveBlock; - - Random random = new Random(); - int baseHeight = 120; // Base ground level - int[] terrainHeight = new int[512]; - - // Generate terrain with gradual height variations - terrainHeight[0] = baseHeight; - for (int i = 1; i < 512; i++) { - int heightChange = random.nextInt(3) - 1; // -1, 0, or +1 - terrainHeight[i] = Math.max(100, Math.min(140, terrainHeight[i - 1] + heightChange)); - } - - // Smooth terrain to avoid unnatural peaks and dips - for (int i = 2; i < 510; i++) { - terrainHeight[i] = (terrainHeight[i - 1] + terrainHeight[i] + terrainHeight[i + 1]) / 3; - } - - // Generate world blocks based on terrain height - for (int i = 0; i < 512; i++) { - int hillHeight = terrainHeight[i]; - - world[hillHeight][i].add(new Block("grass", SpriteLoader.SPRITES.GRASS)); - - // Dirt layers - for (int j = 1; j <= 4; j++) { - if (hillHeight + j < 256) { - world[hillHeight + j][i].add(new Block("dirt", SpriteLoader.SPRITES.DIRT)); - world[hillHeight + j][i].add(new Block("dirt", SpriteLoader.SPRITES.DIRT)); - } - } - - // Stone layers below dirt - for (int j = hillHeight + 5; j < 250; j++) { - world[j][i].add(new Block("stone", SpriteLoader.SPRITES.STONE, 3)); - } - - // Bedrock at the bottom - world[255][i].add(new Block("bedrock", SpriteLoader.SPRITES.BEDROCK)); - } - - // Fill empty spaces with air - for (List[] lists : world) { - for (List list : lists) { - if (list.isEmpty()) { - list.add(new Block("air", SpriteLoader.SPRITES.AIR, true)); - } - } - } - - // Spawn player at a valid starting point - world[terrainHeight[256] - 1][256].add(steveBlock); + Generation.generateWorld(this); } public int calculateHardness(Block block) { @@ -156,11 +98,11 @@ public class Game { } int[] cords2 = getPlayerCords(); - if (world[cords2[1] + 1][cords2[0]].stream().anyMatch(Block::isGhost)) { + if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) { world[cords2[1] + 1][cords2[0]].add(player); world[cords2[1]][cords2[0]].remove(player); - screenRenderer.render(world); + screenRenderer.render(this); } }).start(); } @@ -172,7 +114,7 @@ public class Game { Block breakingBlock = new Block("breaking", SpriteLoader.SPRITES.BREAKING); world[y][x].add(breakingBlock); - screenRenderer.render(world); + screenRenderer.render(this); int hardness = calculateHardness(world[y][x].stream().filter(block -> !block.isGhost()).toList().get(0)); @@ -185,7 +127,7 @@ public class Game { throw new RuntimeException(e); } breakingBlock.setSpriteState(Breaking.BreakingState.SECOND); - screenRenderer.render(world); + screenRenderer.render(this); try { Thread.sleep(hardness * 166L); @@ -193,7 +135,7 @@ public class Game { throw new RuntimeException(e); } breakingBlock.setSpriteState(Breaking.BreakingState.THIRD); - screenRenderer.render(world); + screenRenderer.render(this); try { Thread.sleep(hardness * 166L); @@ -203,9 +145,28 @@ public class Game { mining = false; - world[y][x].clear(); - world[y][x].add(new Block("air", SpriteLoader.SPRITES.AIR, true)); - screenRenderer.render(world); + var blocks = world[y][x]; + for (Block block : blocks) { + if (!block.isMineable()) { + continue; + } + + if (block.getToolVariants().isEmpty()) { + // Add to inv + block.getDrops().forEach(item -> inventory.addItem(item)); + continue; + } + + var toolVariants = block.getToolVariants(); + if (itemInHand.isPresent() && block.getTool().isPresent() && block.getTool().get().equals(itemInHand.get().getType()) && toolVariants.contains(itemInHand.get().getToolVariant())) { + block.getDrops().forEach(item -> inventory.addItem(item)); + } + } + + blocks.clear(); + blocks.add(new Block("air", SpriteLoader.SPRITES.AIR, true, false)); + itemInHand.ifPresent(Item::use); + screenRenderer.render(this); update(screenRenderer); }).start(); @@ -230,10 +191,11 @@ public class Game { int endY = data[3]; return - y >= startY && y < endY && x >= startX && x < endX && + y >= startY && y < endY - 1 && x >= startX && x < endX - 1 && !blocks.stream().allMatch(block -> block.getBlockId().equals("air")) && distanceX <= 5 && distanceY <= 5 - && !(playerX == x && playerY == y); + && !(playerX == x && playerY == y) + && blocks.stream().anyMatch(Block::isMineable); } public void update(ScreenRenderer screenRenderer) { @@ -249,7 +211,7 @@ public class Game { world[cords2[1] + 1][cords2[0]].add(player); world[cords2[1]][cords2[0]].remove(player); - screenRenderer.render(world); + screenRenderer.render(this); } else { break; } diff --git a/src/main/java/cz/jzitnik/game/Generation.java b/src/main/java/cz/jzitnik/game/Generation.java new file mode 100644 index 0000000..e3221af --- /dev/null +++ b/src/main/java/cz/jzitnik/game/Generation.java @@ -0,0 +1,111 @@ +package cz.jzitnik.game; + +import cz.jzitnik.game.items.Item; +import cz.jzitnik.game.items.ItemType; +import cz.jzitnik.game.items.ToolVariant; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class Generation { + public static void generateWorld(Game game) { + var world = game.getWorld(); + initializeWorld(world); + + Block steveBlock = new Block("steve", SpriteLoader.SPRITES.STEVE); + game.setPlayer(steveBlock); + + int[] terrainHeight = generateTerrain(); + populateWorld(world, terrainHeight); + plantTrees(world, terrainHeight); + + // Spawn player at a valid starting point + world[terrainHeight[256] - 1][256].add(steveBlock); + } + + private static void initializeWorld(List[][] world) { + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 512; j++) { + world[i][j] = new ArrayList<>(); + } + } + } + + private static int[] generateTerrain() { + Random random = new Random(); + int baseHeight = 120; + int[] terrainHeight = new int[512]; + + terrainHeight[0] = baseHeight; + for (int i = 1; i < 512; i++) { + int heightChange = random.nextInt(3) - 1; + terrainHeight[i] = Math.max(100, Math.min(140, terrainHeight[i - 1] + heightChange)); + } + + for (int i = 2; i < 510; i++) { + terrainHeight[i] = (terrainHeight[i - 1] + terrainHeight[i] + terrainHeight[i + 1]) / 3; + } + + return terrainHeight; + } + + private static void populateWorld(List[][] world, int[] terrainHeight) { + for (int i = 0; i < 512; i++) { + int hillHeight = terrainHeight[i]; + + world[hillHeight][i].add(new Block("grass", SpriteLoader.SPRITES.GRASS, 1, ItemType.SHOVEL, new ArrayList<>(), List.of(new Item("Dirt block", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_DIRT)))); + + for (int j = 1; j <= 4; j++) { + if (hillHeight + j < 256) { + world[hillHeight + j][i].add(new Block("dirt", SpriteLoader.SPRITES.DIRT, 1, ItemType.SHOVEL, new ArrayList<>())); + } + } + + for (int j = hillHeight + 5; j < 250; j++) { + world[j][i].add(new Block("stone", SpriteLoader.SPRITES.STONE, 15, ItemType.PICKAXE, Arrays.stream(ToolVariant.values()).toList())); + } + + world[255][i].add(new Block("bedrock", SpriteLoader.SPRITES.BEDROCK)); + } + + for (List[] lists : world) { + for (List list : lists) { + if (list.isEmpty()) { + list.add(new Block("air", SpriteLoader.SPRITES.AIR, true, false)); + } + } + } + } + + private static void plantTrees(List[][] world, int[] terrainHeight) { + Random random = new Random(); + for (int i = 10; i < 502; i += random.nextInt(20) + 20) { + int treeBase = terrainHeight[i]; + + if (treeBase - 3 < 0) continue; + + for (int j = 0; j < 3; j++) { + if (treeBase - j >= 0) { + world[treeBase - j - 1][i].add(new Block("oak_log", SpriteLoader.SPRITES.OAK_LOG, 2, ItemType.AXE, List.of(ToolVariant.IRON))); + } + } + + int leafY = treeBase - 4; + + for (int layer = 0; layer < 3; layer++) { + int size = 5 - (layer * 2); + int offsetY = leafY - layer; + + for (int dx = -size / 2; dx <= size / 2; dx++) { + for (int dy = -size / 2; dy <= size / 2; dy++) { + if (i + dx >= 0 && i + dx < world[0].length && offsetY >= 0) { + world[offsetY][i + dx].add(new Block("oak_leaves", SpriteLoader.SPRITES.OAK_LEAF, 1, ItemType.SHEARS, new ArrayList<>())); + } + } + } + } + } + } +} diff --git a/src/main/java/cz/jzitnik/game/Inventory.java b/src/main/java/cz/jzitnik/game/Inventory.java new file mode 100644 index 0000000..e9aaf65 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/Inventory.java @@ -0,0 +1,64 @@ +package cz.jzitnik.game; + +import cz.jzitnik.game.items.InventoryItem; +import cz.jzitnik.game.items.Item; +import cz.jzitnik.tui.Sprite; +import cz.jzitnik.tui.SpriteList; +import lombok.Getter; +import org.jline.terminal.Terminal; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public class Inventory { + private static final int INVENTORY_SIZE_PX = 470; + + private List items = new ArrayList<>(); + + public void addItem(Item item) { + if (!item.isStackable()) { + items.add(new InventoryItem(item)); + return; + } + + var it = items.stream().filter(i -> i.getItem().equals(item)).toList(); + if (!it.isEmpty()) { + it.get(0).setAmount(it.get(0).getAmount() + 1); + + return; + } + + items.add(new InventoryItem(item)); + } + + public void renderHotbar(StringBuilder buffer, SpriteList spriteList, Terminal terminal) { + int termWidth = terminal.getWidth(); + int startLeft = (termWidth / 2) - (INVENTORY_SIZE_PX / 2); + + List sprites = items.subList(0, Math.min(9, items.size())).stream().map(items -> spriteList.getSprite(items.getItem().getSprite())).toList(); + List strings = sprites.stream().map(Sprite::getSprite).toList(); + + for (int i = 0; i < 26; i++) { + // Empty left space + buffer.append("\033[0m ".repeat(Math.max(0, startLeft))); + + if (i == 0 || i == 25) { + buffer.append("\033[38;5;231;48;5;231m▓".repeat(INVENTORY_SIZE_PX - 2)); + } else { + for (int j = 0; j < 9; j++) { + buffer.append("\033[38;5;231;48;5;231m▓".repeat(2)); + if (strings.size() <= j) { + buffer.append("\033[0m ".repeat(50)); + continue; + } + + String x = strings.get(j).split("\n")[i]; + buffer.append(x); + } + } + buffer.append("\033[38;5;231;48;5;231m▓".repeat(2)); + buffer.append("\033[0m\n"); + } + } +} diff --git a/src/main/java/cz/jzitnik/game/MouseHandler.java b/src/main/java/cz/jzitnik/game/MouseHandler.java index 01e1461..ff362a5 100644 --- a/src/main/java/cz/jzitnik/game/MouseHandler.java +++ b/src/main/java/cz/jzitnik/game/MouseHandler.java @@ -94,6 +94,6 @@ public class MouseHandler { screenRenderer.setSelectedBlock(Optional.empty()); } - screenRenderer.render(game.getWorld()); + screenRenderer.render(game); } } diff --git a/src/main/java/cz/jzitnik/game/SpriteLoader.java b/src/main/java/cz/jzitnik/game/SpriteLoader.java index e9c17dd..5b253a7 100644 --- a/src/main/java/cz/jzitnik/game/SpriteLoader.java +++ b/src/main/java/cz/jzitnik/game/SpriteLoader.java @@ -1,6 +1,7 @@ package cz.jzitnik.game; import cz.jzitnik.game.sprites.*; +import cz.jzitnik.game.sprites.items.DirtItem; import cz.jzitnik.tui.Sprite; import cz.jzitnik.tui.SpriteList; @@ -14,7 +15,13 @@ public class SpriteLoader { STEVE, STONE, BEDROCK, - BREAKING + BREAKING, + + OAK_LOG, + OAK_LEAF, + + // Items + ITEM_DIRT } public static final HashMap SPRITES_MAP = new HashMap<>(); @@ -27,6 +34,9 @@ public class SpriteLoader { SPRITES_MAP.put(SPRITES.STEVE, new Steve()); SPRITES_MAP.put(SPRITES.BEDROCK, new Bedrock()); SPRITES_MAP.put(SPRITES.BREAKING, new Breaking()); + SPRITES_MAP.put(SPRITES.OAK_LOG, new OakLog()); + SPRITES_MAP.put(SPRITES.OAK_LEAF, new OakLeaf()); + SPRITES_MAP.put(SPRITES.ITEM_DIRT, new DirtItem()); } public static SpriteList load() { diff --git a/src/main/java/cz/jzitnik/game/items/InventoryItem.java b/src/main/java/cz/jzitnik/game/items/InventoryItem.java new file mode 100644 index 0000000..c446d50 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/items/InventoryItem.java @@ -0,0 +1,18 @@ +package cz.jzitnik.game.items; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +public class InventoryItem { + private int amount; + private Item item; + + public InventoryItem(Item item) { + this.amount = 1; + this.item = item; + } +} diff --git a/src/main/java/cz/jzitnik/game/items/Item.java b/src/main/java/cz/jzitnik/game/items/Item.java index 6f0ab05..0b8c8ce 100644 --- a/src/main/java/cz/jzitnik/game/items/Item.java +++ b/src/main/java/cz/jzitnik/game/items/Item.java @@ -1,22 +1,43 @@ package cz.jzitnik.game.items; -import cz.jzitnik.tui.Sprite; +import cz.jzitnik.game.SpriteLoader; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.Objects; +import java.util.Optional; + @Getter @AllArgsConstructor public class Item { private String name; private ItemType type; - private Sprite sprite; + private Optional toolVariant = Optional.empty(); + private SpriteLoader.SPRITES sprite; + private boolean stackable = true; private int durability; private int miningDecrease = 0; - public Item(String name, ItemType type, Sprite sprite, int durability) { + public Item(String name, ItemType type, SpriteLoader.SPRITES sprite) { this.name = name; this.type = type; this.sprite = sprite; - this.durability = durability; + } + + public void use() { + durability--; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Item item = (Item) o; + return stackable == item.stackable && + durability == item.durability && + miningDecrease == item.miningDecrease && + Objects.equals(name, item.name) && + type == item.type && + Objects.equals(sprite, item.sprite); } } diff --git a/src/main/java/cz/jzitnik/game/items/ItemType.java b/src/main/java/cz/jzitnik/game/items/ItemType.java index 69e62e5..d604e29 100644 --- a/src/main/java/cz/jzitnik/game/items/ItemType.java +++ b/src/main/java/cz/jzitnik/game/items/ItemType.java @@ -2,5 +2,8 @@ package cz.jzitnik.game.items; public enum ItemType { PICKAXE, - SHOVEL + SHOVEL, + AXE, + SHEARS, + BLOCK } diff --git a/src/main/java/cz/jzitnik/game/items/ToolVariant.java b/src/main/java/cz/jzitnik/game/items/ToolVariant.java new file mode 100644 index 0000000..8ee5c8b --- /dev/null +++ b/src/main/java/cz/jzitnik/game/items/ToolVariant.java @@ -0,0 +1,7 @@ +package cz.jzitnik.game.items; + +public enum ToolVariant { + WOODEN, + STONE, + IRON, +} diff --git a/src/main/java/cz/jzitnik/game/sprites/Air.java b/src/main/java/cz/jzitnik/game/sprites/Air.java index 9491f4c..0d426d7 100644 --- a/src/main/java/cz/jzitnik/game/sprites/Air.java +++ b/src/main/java/cz/jzitnik/game/sprites/Air.java @@ -6,9 +6,7 @@ public class Air extends Sprite { public String getSprite() { StringBuilder sprite = new StringBuilder(); for (int i = 0; i < 25; i++) { - for (int j = 0; j < 50; j++) { - sprite.append("\033[0m "); - } + sprite.append("\033[0m ".repeat(50)); sprite.append("\n"); } return sprite.toString(); diff --git a/src/main/java/cz/jzitnik/game/sprites/Breaking.java b/src/main/java/cz/jzitnik/game/sprites/Breaking.java index 073c0ad..d56af4b 100644 --- a/src/main/java/cz/jzitnik/game/sprites/Breaking.java +++ b/src/main/java/cz/jzitnik/game/sprites/Breaking.java @@ -3,6 +3,8 @@ package cz.jzitnik.game.sprites; import cz.jzitnik.tui.ResourceLoader; import cz.jzitnik.tui.Sprite; +import java.util.Objects; + public class Breaking extends Sprite { public enum BreakingState { FIRST, @@ -15,15 +17,15 @@ public class Breaking extends Sprite { } public String getSprite() { - return fix(ResourceLoader.loadResource("breaking/1.ans")); + return fix(Objects.requireNonNull(ResourceLoader.loadResource("breaking/1.ans"))); } public String getSprite(Enum key) { - return fix(ResourceLoader.loadResource(switch (key) { + return fix(Objects.requireNonNull(ResourceLoader.loadResource(switch (key) { case BreakingState.FIRST -> "breaking/1.ans"; case BreakingState.SECOND -> "breaking/2.ans"; case BreakingState.THIRD -> "breaking/3.ans"; default -> throw new IllegalStateException("Unexpected value: " + key); - })); + }))); } } diff --git a/src/main/java/cz/jzitnik/game/sprites/OakLeaf.java b/src/main/java/cz/jzitnik/game/sprites/OakLeaf.java new file mode 100644 index 0000000..4a3553d --- /dev/null +++ b/src/main/java/cz/jzitnik/game/sprites/OakLeaf.java @@ -0,0 +1,20 @@ +package cz.jzitnik.game.sprites; + +import cz.jzitnik.tui.ResourceLoader; +import cz.jzitnik.tui.Sprite; + +import java.util.Objects; + +public class OakLeaf extends Sprite { + private String fix(String x) { + return x.replaceAll("\033\\[38;5;1;48;5;16m", "\033[0m"); + } + + public String getSprite() { + return fix(Objects.requireNonNull(ResourceLoader.loadResource("oak_leaf.ans"))); + } + + public String getSprite(Enum key) { + throw new RuntimeException("Imposible state"); + } +} diff --git a/src/main/java/cz/jzitnik/game/sprites/OakLog.java b/src/main/java/cz/jzitnik/game/sprites/OakLog.java new file mode 100644 index 0000000..1292a91 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/sprites/OakLog.java @@ -0,0 +1,14 @@ +package cz.jzitnik.game.sprites; + +import cz.jzitnik.tui.ResourceLoader; +import cz.jzitnik.tui.Sprite; + +public class OakLog extends Sprite { + public String getSprite() { + return ResourceLoader.loadResource("oak_log.ans"); + } + + public String getSprite(Enum key) { + throw new RuntimeException("Imposible state"); + } +} diff --git a/src/main/java/cz/jzitnik/game/sprites/items/DirtItem.java b/src/main/java/cz/jzitnik/game/sprites/items/DirtItem.java new file mode 100644 index 0000000..173a7df --- /dev/null +++ b/src/main/java/cz/jzitnik/game/sprites/items/DirtItem.java @@ -0,0 +1,14 @@ +package cz.jzitnik.game.sprites.items; + +import cz.jzitnik.tui.ResourceLoader; +import cz.jzitnik.tui.Sprite; + +public class DirtItem extends Sprite { + public String getSprite() { + return ResourceLoader.loadResource("items/dirt.ans"); + } + + public String getSprite(Enum key) { + throw new RuntimeException("Imposible state"); + } +} diff --git a/src/main/java/cz/jzitnik/tui/ScreenMovingCalculationProvider.java b/src/main/java/cz/jzitnik/tui/ScreenMovingCalculationProvider.java index 88de7a3..4e8f636 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 / 2) / spriteHeight; + int viewYRadius = ((terminalHeight - 30) / 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 1379648..84a3699 100644 --- a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java +++ b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java @@ -1,6 +1,7 @@ package cz.jzitnik.tui; import cz.jzitnik.game.Block; +import cz.jzitnik.game.Game; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -35,7 +36,8 @@ public class ScreenRenderer { return null; } - public void render(List[][] world) { + public void render(Game game) { + var world = game.getWorld(); StringBuilder main = new StringBuilder(); main.append("\033[H\033[2J"); @@ -67,7 +69,7 @@ public class ScreenRenderer { endY = Math.max(startY, endY - 1); } - StringBuilder[] lines = new StringBuilder[(endY - startY) * 26]; + StringBuilder[] lines = new StringBuilder[(endY - startY) * 25 + 1]; for (int i = 0; i < lines.length; i++) { lines[i] = new StringBuilder(); } @@ -87,25 +89,19 @@ public class ScreenRenderer { 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("\033[38;5;231;48;5;231m▓".repeat(50)); stringBuilder.append("\n"); for (int i = 0; i < 23; i++) { stringBuilder.append("\033[38;5;231;48;5;231m▓"); stringBuilder.append("\033[38;5;231;48;5;231m▓"); - for (int j = 0; j < 46; j++) { - stringBuilder.append("\033[0m "); - } + stringBuilder.append("\033[0m ".repeat(46)); stringBuilder.append("\033[38;5;231;48;5;231m▓"); 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("\033[38;5;231;48;5;231m▓".repeat(50)); stringBuilder.append("\n"); sprites.add(stringBuilder.toString()); @@ -113,21 +109,29 @@ public class ScreenRenderer { String sprite = SpriteCombiner.combineSprites(sprites.toArray(String[]::new)); - String[] spriteLines = sprite.split("\n"); for (int i = 0; i < spriteLines.length; i++) { - lines[counter * 25 + i].append(spriteLines[i]); + lines[counter * 25 + i].append(spriteLines[i]).append("\033[0m"); } } counter++; } - for (StringBuilder line : lines) { - main.append(line.toString()); - main.append("\n"); + // World + for (int i = 0; i < lines.length; i++) { + main.append(lines[i]); + if (i < lines.length - 1) { + main.append("\n"); + } } + // Empty space between world and hotbar + main.append("\n\n\n"); + + game.getInventory().renderHotbar(main, spriteList, terminal); + System.out.println(main); } + } diff --git a/src/main/resources/textures/items/dirt.ans b/src/main/resources/textures/items/dirt.ans new file mode 100644 index 0000000..3640c2a --- /dev/null +++ b/src/main/resources/textures/items/dirt.ans @@ -0,0 +1,26 @@ +                                                   +                      ▒▒▒▒▒▒▒                      +                  ▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒                  +              ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▒              +          ▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒          +      ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒      +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒   +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░▒░░░   +   ▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░▒▒▒▒░░   +   ▒▓▓▒░░▒▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒▒▒▒▒░▒▒▒░░░▒▒░░   +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒░░▒▒▓░░▒░░░▒░░▒░░▒   +   ▒░░▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░▒░░░▒▒▒░░▓░░░░▒▒   +   ▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒░░▒░░▒░▒░░░▒░░░▒░▒░░░░░░▒░▒▒   +   ▒▒▒▒▒▒▓▒▒░░░░░▒▓▒▒▒░▒▒▒▒░▒▒░▒▒▒░░░░░▒▒▒░░░▒░░   +   ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▓▒▒▒░▒▒░▒▒▒░▒▒▒░░░░▒▒   +   ▒▒▒░▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒░▒▒▒▓░░░░░▒▒▒▒░▒▒▒▓░░   +   ▒▒▒▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒░░░░░▒▒▒▒░░░░░░▒░▒▒▒░░▒   +   ▒░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░▒▒▒░▒░░░▒░▒▒▒░░░░▒▒▒   +   ▒▒▒▒▒▒▒▒▒▒▒▓░▒▒▒▒▒▒▒░▒░▒░░░░░░░▒▒▒▓▒░░░░░▒▒▒▒   +   ▒▒▒░▒▒▒▒▒▒▒▒▓▒▒▒░▒▒▒▒▒░░▒░░▒▒▒▒░░░░░▒▒░░░░▒░░   +      ▒▒▒▒░▒▒▒▒▒▒▒▒▒░▒▒▓▒░░▒░░▒▒▒▒▒▒▒░▒░░▒▒▒▒      +          ▒▓▓░▒▒▒▒░░▒▒▒▒░▒▒░▒▒░▒░░░░░▒▒░░          +              ▒▒▒▒▒▒▒▒▒▒▒░░▒░░▒░░░▓░░              +                  ▒▒▒▒▒▒▒░▒░░░▒▒░                  +                      ▒▒░▒▒░▒                      + \ No newline at end of file diff --git a/src/main/resources/textures/oak_leaf.ans b/src/main/resources/textures/oak_leaf.ans new file mode 100644 index 0000000..71019a0 --- /dev/null +++ b/src/main/resources/textures/oak_leaf.ans @@ -0,0 +1,26 @@ +                         ░                         +                         ░                         +                         ▒                         +                         ▒                         +                      ░                            +                      ░  ▒                         +                      ░  ▒                         +                      ░  ▒                         +                      ░  ▒                         +                      ░  ▒                         +   ▒                     ▒                         +    ░░░                  ░  ▒         ░░░          +                         ░  ▒                      +                         ░                     ░   +                         ░░░░                  ░   +                      ░  ▒                     ░   +                         ░                         +                      ░  ░                         +                            ▒                      +                      ░  ▓                         +                      ░  ▓                         +                      ░  ▓                     ░   +                            ▒                      +                            ▒                      +                                                   + \ No newline at end of file diff --git a/src/main/resources/textures/oak_log.ans b/src/main/resources/textures/oak_log.ans new file mode 100644 index 0000000..2c13290 --- /dev/null +++ b/src/main/resources/textures/oak_log.ans @@ -0,0 +1,26 @@ +▓▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▒▒▓▒▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▓▓▓▓▒▒▒ +▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒ ▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ +▒▒▒▒▒▒░▒▒▒▒▒▒░░░░▒░▒▒▒░▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ +▒▒▒▒░▒░▒▒▒▒▒▒░░ ░▒░▒▒▒░▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ +▓▒▒▒░░░▒▒▒▒▒▒▒▒░▒▒▒▒▒▒ ░░▒▒▒▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ +▒░░░▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒░░░▒▒▒▒▒▒▒░░░▒▒▒▒▒▒▒░░▒▒▒░▒▒ +▒░░░▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒ ░░▒▒▒▒▒▒▒░░░▒▒▒▒▒▒░░░▒▒▒░▒▒ +▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒░░░▒▒▒░░░ +▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒░░░▒▒▒░▒▒ +▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒░░▒▒▒▒░▒░ +▒░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ░░░▒▒▒░░░▒▒▒▒▒▒░░░▒▒▒░░░ +▓▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒░░░▒▒▒░▒▒░▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒░░░ +▓▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒░░░▒▒▒░▒▒░▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒░▒▒ +▓▒▒▒▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒▒▒░░░░▒▒▒░▒▒▒▒▒░░░▒▒▒▒▒▒░▒░ +▓▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒ ░░▒▒▒░░░░▒▒▒░▒▒▒▒▒░░░▒▒▒▒▒▒░▒▒ +▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒░▒▒ +▓▒▒▒▒▒░▒▒▒▒▒░▒▒▒▒▒▒▒▒▒ ░░▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒ +▓▒▒▒▒▒▒▒▒▒▒░░▒▒▒▒▒▒▒▒▒ ░░▒▒▒▒░░░▒▒▒▒▒▒▒▒░▒▒▒▒▒▒░▒▒ +▒▒▒▒░░░▒▒▒▒▒░▒▒▒▒▒▒▒▒▒░▒▒▓▒▒▒░░ ▒▒▒▒▒▒░░░▒▒▒▒▒▒░░░ +▒░░░░░░▒▒▒░░░▒▒▒▒▒▒▒▒▒  ░▒▒▒▒░▒░▒▒▒▒▒▒░░░▒▒▒▒▒▒░▒░ +▒░░░░▒░▒▒▒░░░▒▒▒▒▒▒▒▒▒  ░▒▒▒▒▒▒░▒▒▒▒▒▒░░░▒▒▒▒▒▒░░▒ +▒░░░▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒▒▒▒ +▓▒▒▒░░░▒▒▒▒▒▒░░░▒▒▒▒▒▒░▒▒░░░░▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒ ░░ +▓▒▒▒░░░▒▒▒▒▒▒░▒░▒▒▒▒▒▒░▒▒░░░░▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒░▒▒ +▓▒▒▒▒▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▒▒▒▒░▒▒░▒▒▒▒▒▒▒▒▒░░ ▒▒▒▒▒▒▒▒▒ + \ No newline at end of file