feat: Implemented healthbar

This commit is contained in:
Jakub Žitník 2025-02-28 13:56:55 +01:00
parent e6bcce7765
commit 91882fabc0
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
34 changed files with 659 additions and 78 deletions

12
pom.xml
View File

@ -28,6 +28,18 @@
<artifactId>jline-reader</artifactId> <artifactId>jline-reader</artifactId>
<version>3.20.0</version> <version>3.20.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -0,0 +1,7 @@
package cz.jzitnik.game;
import cz.jzitnik.game.handlers.place.PlaceHandler;
public class Dependencies {
public PlaceHandler placeHandler = new PlaceHandler();
}

View File

@ -1,14 +1,16 @@
package cz.jzitnik.game; package cz.jzitnik.game;
import cz.jzitnik.game.generation.Generation;
import cz.jzitnik.game.items.Item; import cz.jzitnik.game.items.Item;
import cz.jzitnik.game.items.ItemType; import cz.jzitnik.game.items.ItemType;
import cz.jzitnik.game.handlers.place.CustomPlaceHandler;
import cz.jzitnik.game.sprites.Breaking; import cz.jzitnik.game.sprites.Breaking;
import cz.jzitnik.game.sprites.Steve; import cz.jzitnik.game.sprites.Steve;
import cz.jzitnik.game.ui.Chest; import cz.jzitnik.game.ui.Chest;
import cz.jzitnik.game.ui.Furnace; import cz.jzitnik.game.ui.Furnace;
import cz.jzitnik.game.ui.Window; import cz.jzitnik.game.ui.Window;
import cz.jzitnik.game.ui.Inventory; 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.ScreenMovingCalculationProvider;
import cz.jzitnik.tui.ScreenRenderer; import cz.jzitnik.tui.ScreenRenderer;
import lombok.Getter; import lombok.Getter;
@ -20,11 +22,9 @@ import java.util.List;
@Getter @Getter
public class Game { public class Game {
private List<Block>[][] world = new ArrayList[256][512]; @SuppressWarnings("unchecked")
@Setter private final List<Block>[][] world = (List<Block>[][]) new ArrayList[256][512];
private Block player; private final Player player = new Player();
@Setter
private Block player2;
private boolean mining = false; private boolean mining = false;
@Setter @Setter
private Window window = Window.WORLD; private Window window = Window.WORLD;
@ -61,10 +61,10 @@ public class Game {
return; return;
} }
world[cords[1]][cords[0] + 1].add(player2); world[cords[1]][cords[0] + 1].add(player.getPlayerBlock2());
world[cords[1]][cords[0]].remove(player2); world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
world[cords[1]-1][cords[0] + 1].add(player); world[cords[1]-1][cords[0] + 1].add(player.getPlayerBlock1());
world[cords[1]-1][cords[0]].remove(player); world[cords[1]-1][cords[0]].remove(player.getPlayerBlock1());
screenRenderer.render(this); screenRenderer.render(this);
update(screenRenderer); update(screenRenderer);
@ -80,10 +80,10 @@ public class Game {
return; return;
} }
world[cords[1]][cords[0] - 1].add(player2); world[cords[1]][cords[0] - 1].add(player.getPlayerBlock2());
world[cords[1]][cords[0]].remove(player2); world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
world[cords[1]-1][cords[0] - 1].add(player); world[cords[1]-1][cords[0] - 1].add(player.getPlayerBlock1());
world[cords[1]-1][cords[0]].remove(player); world[cords[1]-1][cords[0]].remove(player.getPlayerBlock1());
screenRenderer.render(this); screenRenderer.render(this);
update(screenRenderer); update(screenRenderer);
@ -95,14 +95,14 @@ public class Game {
} }
int[] cords = getPlayerCords(); 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; return;
} }
world[cords[1] - 1][cords[0]].remove(player); world[cords[1] - 1][cords[0]].remove(player.getPlayerBlock1());
world[cords[1] - 1][cords[0]].add(player2); world[cords[1] - 1][cords[0]].add(player.getPlayerBlock2());
world[cords[1] - 2][cords[0]].add(player); world[cords[1] - 2][cords[0]].add(player.getPlayerBlock1());
world[cords[1]][cords[0]].remove(player2); world[cords[1]][cords[0]].remove(player.getPlayerBlock2());
new Thread(() -> { new Thread(() -> {
try { try {
@ -113,10 +113,10 @@ public class Game {
int[] cords2 = getPlayerCords(); int[] cords2 = getPlayerCords();
if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) { if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) {
world[cords2[1] - 1][cords2[0]].remove(player); world[cords2[1] - 1][cords2[0]].remove(player.getPlayerBlock1());
world[cords2[1]][cords2[0]].add(player); world[cords2[1]][cords2[0]].add(player.getPlayerBlock1());
world[cords2[1] + 1][cords2[0]].add(player2); world[cords2[1] + 1][cords2[0]].add(player.getPlayerBlock2());
world[cords2[1]][cords2[0]].remove(player2); world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2());
screenRenderer.render(this); screenRenderer.render(this);
} }
@ -132,7 +132,7 @@ public class Game {
world[y][x].add(breakingBlock); world[y][x].add(breakingBlock);
screenRenderer.render(this); 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; this.mining = true;
@ -187,9 +187,10 @@ public class Game {
} }
} }
blocks.clear(); CustomPlaceHandler customPlaceHandler = gameStates.dependencies.placeHandler.get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId());
blocks.add(new Block("air", SpriteLoader.SPRITES.AIR, true, false)); customPlaceHandler.mine(this, x, y);
inventory.getItemInHand().ifPresent(Item::use); inventory.getItemInHand().ifPresent(Item::use);
screenRenderer.render(this); screenRenderer.render(this);
update(screenRenderer); update(screenRenderer);
@ -233,10 +234,10 @@ public class Game {
int[] cords2 = getPlayerCords(); int[] cords2 = getPlayerCords();
if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) { if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) {
world[cords2[1] - 1][cords2[0]].remove(player); world[cords2[1] - 1][cords2[0]].remove(player.getPlayerBlock1());
world[cords2[1]][cords2[0]].add(player); world[cords2[1]][cords2[0]].add(player.getPlayerBlock1());
world[cords2[1] + 1][cords2[0]].add(player2); world[cords2[1] + 1][cords2[0]].add(player.getPlayerBlock2());
world[cords2[1]][cords2[0]].remove(player2); world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2());
screenRenderer.render(this); screenRenderer.render(this);
} else { } else {
@ -264,7 +265,7 @@ public class Game {
} }
if (!blocks.stream().allMatch(block -> block.getBlockId().equals("air"))) { if (!blocks.stream().allMatch(block -> block.getBlockId().equals("air"))) {
RightClickHandler.handle(x, y, this); RightClickHandler.handle(x, y, this, screenRenderer);
screenRenderer.render(this); screenRenderer.render(this);
return; return;
} }
@ -273,13 +274,14 @@ public class Game {
return; return;
} }
blocks.removeAll(blocks.stream().filter(block -> block.getBlockId().equals("air")).toList()); Item item = inventory.getItemInHand().get();
blocks.add(inventory.getItemInHand().get().getBlock().get());
inventory.decreaseItemInHand(); CustomPlaceHandler placeHandler = gameStates.dependencies.placeHandler.get(item.getId());
if (placeHandler.place(this, x, y)) {
screenRenderer.render(this); screenRenderer.render(this);
} }
}
public void changeSlot(int slot, ScreenRenderer screenRenderer) { public void changeSlot(int slot, ScreenRenderer screenRenderer) {
if (window != Window.WORLD) { if (window != Window.WORLD) {

View File

@ -4,10 +4,12 @@ import cz.jzitnik.game.ui.CraftingTable;
public class GameStates { public class GameStates {
public CraftingTable craftingTable; public CraftingTable craftingTable;
public Dependencies dependencies;
public int clickX = -1; public int clickX = -1;
public int clickY = -1; public int clickY = -1;
public GameStates(Game game) { public GameStates(Game game) {
craftingTable = new CraftingTable(game); craftingTable = new CraftingTable(game);
dependencies = new Dependencies();
} }
} }

View File

@ -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;
}

View File

@ -21,10 +21,12 @@ public class SpriteLoader {
CHEST, CHEST,
CRAFTING_TABLE, CRAFTING_TABLE,
COAL_ORE,
OAK_LOG, OAK_LOG,
OAK_LEAF, OAK_LEAF,
OAK_PLANKS, OAK_PLANKS,
OAK_DOOR,
WOODEN_PICKAXE, WOODEN_PICKAXE,
STONE_PICKAXE, STONE_PICKAXE,
@ -41,9 +43,13 @@ public class SpriteLoader {
ITEM_COBBLESTONE, ITEM_COBBLESTONE,
ITEM_STONE, ITEM_STONE,
ITEM_FURNACE, ITEM_FURNACE,
ITEM_OAK_DOOR,
ITEM_CRAFTING_TABLE, ITEM_CRAFTING_TABLE,
ITEM_CHEST ITEM_CHEST,
HEART,
HUNGER
} }
public static final HashMap<SPRITES, Sprite> SPRITES_MAP = new HashMap<>(); public static final HashMap<SPRITES, Sprite> SPRITES_MAP = new HashMap<>();
@ -63,6 +69,8 @@ public class SpriteLoader {
SPRITES_MAP.put(SPRITES.CHEST, new SimpleSprite("chest.ans")); SPRITES_MAP.put(SPRITES.CHEST, new SimpleSprite("chest.ans"));
SPRITES_MAP.put(SPRITES.COBBLESTONE, new SimpleSprite("cobblestone.ans")); SPRITES_MAP.put(SPRITES.COBBLESTONE, new SimpleSprite("cobblestone.ans"));
SPRITES_MAP.put(SPRITES.FURNACE, new Furnace()); 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.WOODEN_PICKAXE, new SimpleSprite("items/wooden_pickaxe.ans"));
SPRITES_MAP.put(SPRITES.STONE_PICKAXE, new SimpleSprite("items/stone_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_STONE, new SimpleSprite("items/stone.ans"));
SPRITES_MAP.put(SPRITES.ITEM_CHEST, new SimpleSprite("items/chest.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_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<SPRITES> load() { public static SpriteList<SPRITES> load() {

View File

@ -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();
}

View File

@ -94,6 +94,18 @@ public class CraftingRecipeList {
{"cobblestone", null, "cobblestone"}, {"cobblestone", null, "cobblestone"},
{"cobblestone", "cobblestone", "cobblestone"} {"cobblestone", "cobblestone", "cobblestone"}
}, () -> new InventoryItem(1, ItemBlockSupplier.Items.furnace()))); }, () -> 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<CraftingRecipe> getRecipe(String[] r) { public static Optional<CraftingRecipe> getRecipe(String[] r) {

View File

@ -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<Block>[][] world, int[] terrainHeight) {
}
}

View File

@ -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.ItemBlockSupplier;
import cz.jzitnik.game.items.ItemType; import cz.jzitnik.game.items.ItemType;
import cz.jzitnik.game.sprites.Steve; import cz.jzitnik.game.sprites.Steve;
@ -18,8 +21,8 @@ public class Generation {
Block steveBlock2 = new Block("steve", SpriteLoader.SPRITES.STEVE); Block steveBlock2 = new Block("steve", SpriteLoader.SPRITES.STEVE);
steveBlock2.setSpriteState(Steve.SteveState.SECOND); steveBlock2.setSpriteState(Steve.SteveState.SECOND);
game.setPlayer(steveBlock); game.getPlayer().setPlayerBlock1(steveBlock);
game.setPlayer2(steveBlock2); game.getPlayer().setPlayerBlock2(steveBlock2);
int[] terrainHeight = generateTerrain(); int[] terrainHeight = generateTerrain();
populateWorld(world, terrainHeight); populateWorld(world, terrainHeight);
@ -77,9 +80,7 @@ public class Generation {
for (List<Block>[] lists : world) { for (List<Block>[] lists : world) {
for (List<Block> list : lists) { for (List<Block> list : lists) {
if (list.isEmpty()) { list.addFirst(new Block("air", SpriteLoader.SPRITES.AIR, true, false));
list.add(new Block("air", SpriteLoader.SPRITES.AIR, true, false));
}
} }
} }
} }

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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<String, CustomPlaceHandler> 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<Class<?>> 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();
}
}
}
}
}

View File

@ -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());
}
}

View File

@ -1,8 +1,10 @@
package cz.jzitnik.tui; package cz.jzitnik.game.handlers.rightclick;
import cz.jzitnik.game.Block; import cz.jzitnik.game.Block;
import cz.jzitnik.game.Game; import cz.jzitnik.game.Game;
import cz.jzitnik.game.handlers.place.handlers.DoorPlaceHandler;
import cz.jzitnik.game.ui.Window; import cz.jzitnik.game.ui.Window;
import cz.jzitnik.tui.ScreenRenderer;
import java.util.HashMap; import java.util.HashMap;
@ -12,7 +14,7 @@ public class RightClickHandler {
void apply(T t, U u); 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()) { if (game.isMining()) {
return; return;
} }
@ -21,6 +23,7 @@ public class RightClickHandler {
functionMap.put("crafting_table", game.getGameStates().craftingTable::render); functionMap.put("crafting_table", game.getGameStates().craftingTable::render);
functionMap.put("chest", (Integer ignored, Integer ignored2) -> game.setWindow(Window.CHEST)); functionMap.put("chest", (Integer ignored, Integer ignored2) -> game.setWindow(Window.CHEST));
functionMap.put("furnace", (Integer ignored, Integer ignored2) -> game.setWindow(Window.FURNACE)); 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().clickX = x;
game.getGameStates().clickY = y; game.getGameStates().clickY = y;

View File

@ -36,6 +36,9 @@ public class ItemBlockSupplier {
public static Item furnace(Block ref) { public static Item furnace(Block ref) {
return new Item("furnace", "Furnace", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_FURNACE, 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 { public static class Blocks {
@ -85,6 +88,14 @@ public class ItemBlockSupplier {
block.setDrops(List.of(Helper.furnace(block))); block.setDrops(List.of(Helper.furnace(block)));
return 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 // I hate this but whatever
@ -131,5 +142,8 @@ public class ItemBlockSupplier {
public static Item furnace() { public static Item furnace() {
return Helper.furnace(Blocks.furnace()); return Helper.furnace(Blocks.furnace());
} }
public static Item oakDoor() {
return Helper.oakDoor(Blocks.oakDoor());
}
} }
} }

View File

@ -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);
}
);
}
}

View File

@ -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);
}
);
}
}

View File

@ -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);
}
);
}
}

View File

@ -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");
}
}

View File

@ -6,7 +6,7 @@ public class ScreenMovingCalculationProvider {
int spriteHeight = 25; int spriteHeight = 25;
int viewXRadius = (terminalWidth / 2) / spriteWidth; 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 // Ensure at least one sprite is visible
viewXRadius = Math.max(viewXRadius, 1); viewXRadius = Math.max(viewXRadius, 1);

View File

@ -5,6 +5,7 @@ import cz.jzitnik.game.Game;
import cz.jzitnik.game.sprites.Steve; import cz.jzitnik.game.sprites.Steve;
import cz.jzitnik.game.ui.Chest; import cz.jzitnik.game.ui.Chest;
import cz.jzitnik.game.ui.Furnace; import cz.jzitnik.game.ui.Furnace;
import cz.jzitnik.game.ui.Healthbar;
import cz.jzitnik.tui.utils.SpriteCombiner; import cz.jzitnik.tui.utils.SpriteCombiner;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -141,6 +142,8 @@ public class ScreenRenderer {
// Empty space between world and hotbar // Empty space between world and hotbar
main.append("\n\n\n"); main.append("\n\n\n");
Healthbar.render(main, spriteList, terminal, game);
game.getInventory().renderHotbar(main, spriteList, terminal, false); game.getInventory().renderHotbar(main, spriteList, terminal, false);
} }
} }

View File

@ -23,11 +23,4 @@ public class SpriteList<E extends Enum<E>> {
public Sprite getSprite(E key) { public Sprite getSprite(E key) {
return sprites.get(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);
}
} }

View File

@ -0,0 +1,25 @@
           
       
                
               
             
           
       
               
              
             
               
     
             
          
         
              
           
            
           
            
            
            
         
       
       

View File

@ -0,0 +1,9 @@
              
           
         
         
         
           
             
               
                 

View File

@ -0,0 +1,9 @@
              
           
         
         
         
           
             
               
                 

View File

@ -0,0 +1,9 @@
               
              
            
           
            
             
            
               
               

View File

@ -0,0 +1,9 @@
                
              
            
           
            
            
            
               
               

View File

@ -1,26 +1,25 @@
                                                                                                     
                                                                                                
                            ▒                                                                     
                         ░░░░      ░░░                                                        
                            ░░░░░░░                                                          
                         ░░░░░░░░░░                                                          
                         ░░░░░░░░░░                                                      
                      ░░░░░░░░░░░░░   ░░░                                               
                      ░░░░░░░░░░░░░░░░                                                    
                      ░░░░░░░░░░░░░░░░                                                    
                                ░░░░░░░░░                                                
                            ░░░░   ░░░░░░                                                 
                            ░░░░   ░░░░░░                                                 
                         ░░░   ▒                                                             
                   ▓▓▓░░░    ░░░   ░░░░░░                                                       
                      ░░░   ░                                                                   
                   ░░░                                                                          
                   ░░░                                                                          
                ░░░                                                                             
             ░░░                                                                                 
             ░░░                                                                               
      ░   ░░░                                                                                   
      ░                                                                                          
      ░                                                                                          
                                                                                                     


View File

@ -0,0 +1,25 @@
                    
                     
                   
                  
                
                       
                   
                
                     
                      
                   
                     
                        
             
                 
                    
                  
                 
                       
                
                      
                   
                   
                
   

View File

@ -0,0 +1,25 @@
                                             
                                             
                                             
                                             
                                             
                                            
                                            
                                             
                                             
                                             
                                             
                                             
                                             
                                             
                                             
                                            
                                             
                                             
                                             
                                             
                                             
                                             
                                            
                                           
                                           

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                     
                     
                                          
                                          
                                          
                       
                          
                                          
                                          
                             
                         
                           
                             
                              
                          
                     
                          
                              
                              
                     
                     
                    

View File

@ -0,0 +1,25 @@
            
              
     
             
               
                                    
                                    
                                     
                                    
                   
                 
              
              
                                    
                                     
                                    
                                    
                 
                  
               
                   
             
                
                  
                  

View File

@ -0,0 +1,25 @@