feat: Implemented hotbar and started working on building

This commit is contained in:
Jakub Žitník 2025-02-20 10:19:01 +01:00
parent 3d4a453e1b
commit 07ef87d42b
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
18 changed files with 319 additions and 16 deletions

View File

@ -4,12 +4,14 @@ import cz.jzitnik.game.items.Item;
import cz.jzitnik.game.items.ItemType; import cz.jzitnik.game.items.ItemType;
import cz.jzitnik.game.items.ToolVariant; import cz.jzitnik.game.items.ToolVariant;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@Getter @Getter
@Setter
public class Block { public class Block {
private String blockId; private String blockId;
private SpriteLoader.SPRITES sprite; private SpriteLoader.SPRITES sprite;

View File

@ -1,6 +1,7 @@
package cz.jzitnik.game; package cz.jzitnik.game;
import cz.jzitnik.game.items.Item; import cz.jzitnik.game.items.Item;
import cz.jzitnik.game.items.ItemType;
import cz.jzitnik.game.sprites.Breaking; import cz.jzitnik.game.sprites.Breaking;
import cz.jzitnik.tui.ScreenMovingCalculationProvider; import cz.jzitnik.tui.ScreenMovingCalculationProvider;
import cz.jzitnik.tui.ScreenRenderer; import cz.jzitnik.tui.ScreenRenderer;
@ -10,7 +11,6 @@ import org.jline.terminal.Terminal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
@Getter @Getter
public class Game { public class Game {
@ -20,7 +20,6 @@ public class Game {
private boolean mining = false; private boolean mining = false;
private Inventory inventory = new Inventory(); private Inventory inventory = new Inventory();
private Optional<Item> itemInHand = Optional.empty();
public Game() { public Game() {
Generation.generateWorld(this); Generation.generateWorld(this);
@ -28,8 +27,8 @@ public class Game {
public int calculateHardness(Block block) { public int calculateHardness(Block block) {
int holdingDecrease = 0; int holdingDecrease = 0;
if (itemInHand.isPresent() && block.getTool().isPresent() && itemInHand.get().getType().equals(block.getTool().get())) { if (inventory.getItemInHand().isPresent() && block.getTool().isPresent() && inventory.getItemInHand().get().getType().equals(block.getTool().get())) {
holdingDecrease = itemInHand.get().getMiningDecrease(); holdingDecrease = inventory.getItemInHand().get().getMiningDecrease();
} }
int decrease = block.getHardness() - holdingDecrease; int decrease = block.getHardness() - holdingDecrease;
@ -158,14 +157,14 @@ public class Game {
} }
var toolVariants = block.getToolVariants(); var toolVariants = block.getToolVariants();
if (itemInHand.isPresent() && block.getTool().isPresent() && block.getTool().get().equals(itemInHand.get().getType()) && toolVariants.contains(itemInHand.get().getToolVariant())) { if (inventory.getItemInHand().isPresent() && inventory.getItemInHand().get().getToolVariant().isPresent() && block.getTool().isPresent() && block.getTool().get().equals(inventory.getItemInHand().get().getType()) && toolVariants.contains(inventory.getItemInHand().get().getToolVariant().get())) {
block.getDrops().forEach(item -> inventory.addItem(item)); block.getDrops().forEach(item -> inventory.addItem(item));
} }
} }
blocks.clear(); blocks.clear();
blocks.add(new Block("air", SpriteLoader.SPRITES.AIR, true, false)); blocks.add(new Block("air", SpriteLoader.SPRITES.AIR, true, false));
itemInHand.ifPresent(Item::use); inventory.getItemInHand().ifPresent(Item::use);
screenRenderer.render(this); screenRenderer.render(this);
update(screenRenderer); update(screenRenderer);
@ -217,4 +216,21 @@ public class Game {
} }
} }
} }
public void build(int x, int y, ScreenRenderer screenRenderer) {
var blocks = world[y][x];
if (!blocks.stream().allMatch(Block::isGhost)) {
return;
}
if (!(inventory.getItemInHand().isPresent() && inventory.getItemInHand().get().getType() == ItemType.BLOCK)) {
return;
}
blocks.removeAll(blocks.stream().filter(block -> block.getBlockId().equals("air")).toList());
blocks.add(inventory.getItemInHand().get().getBlock().get());
screenRenderer.render(this);
}
} }

View File

@ -10,6 +10,22 @@ import java.util.List;
import java.util.Random; import java.util.Random;
public class Generation { public class Generation {
private static Block dirt() {
var block = new Block("dirt", SpriteLoader.SPRITES.DIRT, 1, ItemType.SHOVEL, new ArrayList<>());
block.setDrops(List.of(new Item("Dirt block", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_DIRT, block)));
return block;
}
private static Block grass() {
var block = new Block("grass", SpriteLoader.SPRITES.GRASS, 1, ItemType.SHOVEL, new ArrayList<>());
block.setDrops(List.of(new Item("Dirt block", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_DIRT, block)));
return block;
}
private static Block stone() {
var block = new Block("stone", SpriteLoader.SPRITES.STONE, 15, ItemType.PICKAXE, Arrays.stream(ToolVariant.values()).toList());
block.setDrops(List.of(new Item("Stone block", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_DIRT, block)));
return block;
}
public static void generateWorld(Game game) { public static void generateWorld(Game game) {
var world = game.getWorld(); var world = game.getWorld();
initializeWorld(world); initializeWorld(world);
@ -55,16 +71,16 @@ public class Generation {
for (int i = 0; i < 512; i++) { for (int i = 0; i < 512; i++) {
int hillHeight = terrainHeight[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)))); world[hillHeight][i].add(grass());
for (int j = 1; j <= 4; j++) { for (int j = 1; j <= 4; j++) {
if (hillHeight + j < 256) { if (hillHeight + j < 256) {
world[hillHeight + j][i].add(new Block("dirt", SpriteLoader.SPRITES.DIRT, 1, ItemType.SHOVEL, new ArrayList<>())); world[hillHeight + j][i].add(dirt());
} }
} }
for (int j = hillHeight + 5; j < 250; j++) { 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[j][i].add(stone());
} }
world[255][i].add(new Block("bedrock", SpriteLoader.SPRITES.BEDROCK)); world[255][i].add(new Block("bedrock", SpriteLoader.SPRITES.BEDROCK));

View File

@ -2,19 +2,27 @@ package cz.jzitnik.game;
import cz.jzitnik.game.items.InventoryItem; import cz.jzitnik.game.items.InventoryItem;
import cz.jzitnik.game.items.Item; import cz.jzitnik.game.items.Item;
import cz.jzitnik.tui.Sprite; import cz.jzitnik.tui.SpriteCombiner;
import cz.jzitnik.game.sprites.Number;
import cz.jzitnik.tui.SpriteList; import cz.jzitnik.tui.SpriteList;
import lombok.Getter; import lombok.Getter;
import org.jline.terminal.Terminal; import org.jline.terminal.Terminal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
@Getter @Getter
public class Inventory { public class Inventory {
private static final int INVENTORY_SIZE_PX = 470; private static final int INVENTORY_SIZE_PX = 470;
private List<InventoryItem> items = new ArrayList<>(); private List<InventoryItem> items = new ArrayList<>();
private int itemInhHandIndex = 0;
public Optional<Item> getItemInHand() {
return Optional.empty();
}
public void addItem(Item item) { public void addItem(Item item) {
if (!item.isStackable()) { if (!item.isStackable()) {
@ -32,12 +40,53 @@ public class Inventory {
items.add(new InventoryItem(item)); items.add(new InventoryItem(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));
}
public void renderHotbar(StringBuilder buffer, SpriteList spriteList, Terminal terminal) { public void renderHotbar(StringBuilder buffer, SpriteList spriteList, Terminal terminal) {
int termWidth = terminal.getWidth(); int termWidth = terminal.getWidth();
int startLeft = (termWidth / 2) - (INVENTORY_SIZE_PX / 2); int startLeft = (termWidth / 2) - (INVENTORY_SIZE_PX / 2);
List<Sprite> sprites = items.subList(0, Math.min(9, items.size())).stream().map(items -> spriteList.getSprite(items.getItem().getSprite())).toList(); List<String> sprites = items.subList(0, Math.min(9, items.size())).stream().map(
List<String> strings = sprites.stream().map(Sprite::getSprite).toList(); items -> SpriteCombiner.combineTwoSprites(
spriteList.getSprite(items.getItem().getSprite()).getSprite(),
getNumberSprite(items.getAmount())
)
).toList();
for (int i = 0; i < 26; i++) { for (int i = 0; i < 26; i++) {
// Empty left space // Empty left space
@ -48,12 +97,12 @@ public class Inventory {
} else { } else {
for (int j = 0; j < 9; j++) { for (int j = 0; j < 9; j++) {
buffer.append("\033[38;5;231;48;5;231m▓".repeat(2)); buffer.append("\033[38;5;231;48;5;231m▓".repeat(2));
if (strings.size() <= j) { if (sprites.size() <= j) {
buffer.append("\033[0m ".repeat(50)); buffer.append("\033[0m ".repeat(50));
continue; continue;
} }
String x = strings.get(j).split("\n")[i]; String x = sprites.get(j).split("\n")[i];
buffer.append(x); buffer.append(x);
} }
} }

View File

@ -22,6 +22,11 @@ public class MouseHandler {
return; return;
} }
if (mouseEvent.getButton() == MouseEvent.Button.Button2 && mouseEvent.getType() == MouseEvent.Type.Pressed) {
rightClick(mouseEvent);
return;
}
if (mouseEvent.getType() == MouseEvent.Type.Moved) { if (mouseEvent.getType() == MouseEvent.Type.Moved) {
move(mouseEvent); move(mouseEvent);
} }
@ -51,6 +56,27 @@ public class MouseHandler {
} }
} }
private void rightClick(MouseEvent mouseEvent) {
int mouseX = mouseEvent.getX();
int mouseY = mouseEvent.getY();
int[] cords = game.getPlayerCords();
if (cords == null) return;
int playerX = cords[0];
int playerY = cords[1];
int[] data = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length);
int startX = data[0];
int startY = data[2];
int blockX = startX + (mouseX / 50); // 50 chars wide per sprite
int blockY = startY + (mouseY / 25); // 25 lines high per sprite
game.build(blockX, blockY, screenRenderer);
}
private void move(MouseEvent mouseEvent) { private void move(MouseEvent mouseEvent) {
if (game.isMining()) { if (game.isMining()) {
return; return;

View File

@ -1,5 +1,6 @@
package cz.jzitnik.game.items; package cz.jzitnik.game.items;
import cz.jzitnik.game.Block;
import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.SpriteLoader;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@ -17,11 +18,13 @@ public class Item {
private boolean stackable = true; private boolean stackable = true;
private int durability; private int durability;
private int miningDecrease = 0; private int miningDecrease = 0;
private Optional<Block> block;
public Item(String name, ItemType type, SpriteLoader.SPRITES sprite) { public Item(String name, ItemType type, SpriteLoader.SPRITES sprite, Block block) {
this.name = name; this.name = name;
this.type = type; this.type = type;
this.sprite = sprite; this.sprite = sprite;
this.block = Optional.of(block);
} }
public void use() { public void use() {

View File

@ -0,0 +1,61 @@
package cz.jzitnik.game.sprites;
import cz.jzitnik.tui.ResourceLoader;
import cz.jzitnik.tui.Sprite;
import java.util.Objects;
public class Number extends Sprite {
public enum NumberState {
ZERO,
ONE,
TWO,
THREE,
FOUR,
FIVE,
SIX,
SEVEN,
EIGHT,
NINE,
}
public static String fixForHotbar(String str, int place) {
var parts = str.split("\n");
// Make it 50x25
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 24 - 12; i++) {
stringBuilder.append("\033[0m ".repeat(50)).append("\n");
}
for (int i = 0; i < 12; i++) {
stringBuilder.append("\033[0m ".repeat(50 - 12 * place)).append(parts[i]).append("\033[0m ".repeat(12 * (place-1))).append("\n");
}
stringBuilder.append("\033[0m ".repeat(50)).append("\n");
return stringBuilder.toString();
}
private String fix(String x) {
return x;
//return x.replaceAll("\033\\[38;5;1;48;5;16m", "\033[0m").replaceAll("\033\\[38;5;16;48;5;16m▓", "\033[0m "); // TODO: Fix issue with sprite size decreasing
}
public String getSprite() {
return fix(Objects.requireNonNull(ResourceLoader.loadResource("breaking/1.ans")));
}
public String getSprite(Enum key) {
return fix(Objects.requireNonNull(ResourceLoader.loadResource(switch (key) {
case NumberState.ZERO -> "numbers/0.ans";
case NumberState.ONE -> "numbers/1.ans";
case NumberState.TWO -> "numbers/2.ans";
case NumberState.THREE -> "numbers/3.ans";
case NumberState.FOUR -> "numbers/4.ans";
case NumberState.FIVE -> "numbers/5.ans";
case NumberState.SIX -> "numbers/6.ans";
case NumberState.SEVEN -> "numbers/7.ans";
case NumberState.EIGHT -> "numbers/8.ans";
case NumberState.NINE -> "numbers/9.ans";
default -> throw new IllegalStateException("Unexpected value: " + key);
})));
}
}

View File

@ -16,7 +16,7 @@ public class SpriteCombiner {
} }
private static String combineTwoSprites(String sprite1, String sprite2) { public static String combineTwoSprites(String sprite1, String sprite2) {
String[] rows1 = sprite1.split("\n"); String[] rows1 = sprite1.split("\n");
String[] rows2 = sprite2.split("\n"); String[] rows2 = sprite2.split("\n");

View File

@ -0,0 +1,13 @@
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,13 @@
   ▒▓▓▓▒    
   ▒▓▓▓▒    
▓▓▓▓▓▓▓▒    
▓▓▓▓▓▓▓▒    
   ▒▓▓▓▒    
   ▒▓▓▓▒    
   ▒▓▓▓▒    
   ▒▓▓▓▒    
   ▒▓▓▓▒    
   ▒▓▓▓▒    
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,13 @@
   ▒▓▓▓▒    
   ▒▓▓▓▒    
▓▓▓▒   ▒▓▓▓▓
▓▓▓▒   ▒▓▓▓▓
       ▒▓▓▓▓
       ▒▓▓▓▓
   ▒▓▓▓▒    
   ▒▓▓▓▒    
▓▓▓▒        
▓▓▓▒        
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,13 @@
▓▓▓▓▓▓▓▒    
▓▓▓▓▓▓▓▒    
       ▒▓▓▓▓
       ▒▓▓▓▓
   ▒▓▓▓▒    
   ▒▓▓▓▒    
       ▒▓▓▓▓
       ▒▓▓▓▓
▓▓▓▒   ▒▓▓▓▓
▓▓▓▒   ▒▓▓▓▓
   ▒▓▓▓▒    
   ▒▓▓▓▒    


View File

@ -0,0 +1,13 @@
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,13 @@
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,13 @@
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,13 @@
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,13 @@
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓


View File

@ -0,0 +1,13 @@
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓