diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index 8ac68f7..62c94f9 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -634,4 +634,8 @@ public class Game { } } } + + public Object getContext(Class clazz) { + return gameStates.globalContextProvider.getMap(clazz); + } } diff --git a/src/main/java/cz/jzitnik/game/SpriteLoader.java b/src/main/java/cz/jzitnik/game/SpriteLoader.java index 09df397..dff0b3f 100644 --- a/src/main/java/cz/jzitnik/game/SpriteLoader.java +++ b/src/main/java/cz/jzitnik/game/SpriteLoader.java @@ -336,7 +336,7 @@ public class SpriteLoader { SPRITES_MAP.put(SPRITES.WOODEN_SHOVEL, new SimpleSprite("items/wooden_shovel.ans")); SPRITES_MAP.put(SPRITES.WOODEN_HOE, new SimpleSprite("items/wooden_hoe.ans")); - SPRITES_MAP.put(SPRITES.STONE_SWORD, new SimpleSprite("items/wooden_sword.ans")); + SPRITES_MAP.put(SPRITES.STONE_SWORD, new SimpleSprite("items/stone_sword.ans")); SPRITES_MAP.put(SPRITES.STONE_PICKAXE, new SimpleSprite("items/stone_pickaxe.ans")); SPRITES_MAP.put(SPRITES.STONE_AXE, new SimpleSprite("items/stone_axe.ans")); SPRITES_MAP.put(SPRITES.STONE_SHOVEL, new SimpleSprite("items/stone_shovel.ans")); diff --git a/src/main/java/cz/jzitnik/game/annotations/GlobalContext.java b/src/main/java/cz/jzitnik/game/annotations/GlobalContext.java new file mode 100644 index 0000000..24cbd1f --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/GlobalContext.java @@ -0,0 +1,9 @@ +package cz.jzitnik.game.annotations; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface GlobalContext { + +} diff --git a/src/main/java/cz/jzitnik/game/blocks/Bed.java b/src/main/java/cz/jzitnik/game/blocks/Bed.java new file mode 100644 index 0000000..0778cff --- /dev/null +++ b/src/main/java/cz/jzitnik/game/blocks/Bed.java @@ -0,0 +1,66 @@ +package cz.jzitnik.game.blocks; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.RightClickLogic; +import cz.jzitnik.game.context.list.BedSleepGlobalContext; +import cz.jzitnik.game.handlers.rightclick.RightClickHandler; +import cz.jzitnik.tui.ScreenRenderer; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +@RightClickLogic +public class Bed implements RightClickHandler { + + @Override + public void onBlockRightClick(int x, int y, Game game, ScreenRenderer screenRenderer) { + BedSleepGlobalContext bedSleepGlobalContext = + (BedSleepGlobalContext) game.getContext(BedSleepGlobalContext.class); + + if (bedSleepGlobalContext.isSleeping() || game.getDaytime() < 200 || game.getDaytime() > 500) { + return; // already in progress + } + + bedSleepGlobalContext.setSleeping(true); + + int targetTime = 500; + int step = 10; + long delayMillis = 50; // animation speed + + // Create a brand-new scheduler for this run + ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + final ScheduledFuture[] futureHolder = new ScheduledFuture[1]; + + Runnable sleepTask = () -> { + int currentTime = game.getDaytime(); + + if (currentTime == targetTime) { + // cancel this repeating task + shut down the scheduler + futureHolder[0].cancel(false); + scheduler.shutdown(); + bedSleepGlobalContext.setSleeping(false); + return; + } + + int nextTime = (currentTime + step) % 600; + + if (currentTime < targetTime && nextTime >= targetTime) { + nextTime = targetTime; + } + + if (nextTime < currentTime && nextTime >= targetTime) { + nextTime = targetTime; + } + + game.setDaytime(nextTime); + screenRenderer.render(game); + }; + + // Schedule the task and keep its handle + futureHolder[0] = scheduler.scheduleAtFixedRate( + sleepTask, 0, delayMillis, TimeUnit.MILLISECONDS); + } +} diff --git a/src/main/java/cz/jzitnik/game/blocks/Furnace.java b/src/main/java/cz/jzitnik/game/blocks/Furnace.java index e7722c1..0b8e94b 100644 --- a/src/main/java/cz/jzitnik/game/blocks/Furnace.java +++ b/src/main/java/cz/jzitnik/game/blocks/Furnace.java @@ -22,7 +22,7 @@ import java.util.Optional; @RightClickLogic public class Furnace implements RightClickHandler, Serializable { - private final Block block; + private Block block; private final InventoryItem[] items = new InventoryItem[2]; private InventoryItem outputItem; private int size; @@ -37,6 +37,8 @@ public class Furnace implements RightClickHandler, Serializable { this.block = block; } + public Furnace() {} + public void render(Game game, StringBuilder buffer, Terminal terminal, SpriteList spriteList) { int widthPixels = COLUMN_AMOUNT * (CELL_WIDTH + BORDER_SIZE) + BORDER_SIZE; var inventory = game.getInventory(); @@ -193,7 +195,7 @@ public class Furnace implements RightClickHandler, Serializable { // Add smelt item Thread thread1 = new Thread(() -> { - while (true) { + do { try { Thread.sleep(10000); } catch (InterruptedException e) { @@ -215,10 +217,7 @@ public class Furnace implements RightClickHandler, Serializable { rerender(game, screenRenderer); } - if (items[0] == null || !smelting) { - break; - } - } + } while (items[0] != null && smelting); }); Thread thread2 = new Thread(() -> { diff --git a/src/main/java/cz/jzitnik/game/context/GlobalContextProvider.java b/src/main/java/cz/jzitnik/game/context/GlobalContextProvider.java new file mode 100644 index 0000000..8c37962 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/context/GlobalContextProvider.java @@ -0,0 +1,33 @@ +package cz.jzitnik.game.context; + +import cz.jzitnik.game.annotations.GlobalContext; +import org.reflections.Reflections; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class GlobalContextProvider { + private final Map data = new HashMap<>(); + private boolean loaded = false; + + public Object getMap(Class clazz) { + if (!loaded) { + Reflections reflections = new Reflections("cz.jzitnik.game.context"); + Set> handlerClasses = reflections.getTypesAnnotatedWith(GlobalContext.class); + + for (Class claz : handlerClasses) { + try { + Object instance = claz.getDeclaredConstructor().newInstance(); + data.put(claz, instance); + } catch (Exception e) { + e.printStackTrace(); + } + } + + loaded = true; + } + + return data.get(clazz); + } +} diff --git a/src/main/java/cz/jzitnik/game/context/list/BedSleepGlobalContext.java b/src/main/java/cz/jzitnik/game/context/list/BedSleepGlobalContext.java new file mode 100644 index 0000000..a3e10e2 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/context/list/BedSleepGlobalContext.java @@ -0,0 +1,12 @@ +package cz.jzitnik.game.context.list; + +import cz.jzitnik.game.annotations.GlobalContext; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@GlobalContext +public class BedSleepGlobalContext { + private boolean sleeping = false; +} diff --git a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java index af3c569..7589c65 100644 --- a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java +++ b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java @@ -92,22 +92,33 @@ public class CraftingRecipeList { } public static boolean are2DArraysIdentical(String[][] array1, String[][] array2, boolean usingRegex) { + if (array1 == null || array2 == null) { + return array1 == array2; + } + if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; i++) { + if (array1[i] == null || array2[i] == null) { + if (array1[i] != array2[i]) return false; + continue; + } + 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 - && (usingRegex ? !array2[i][j].matches(array1[i][j]) : !array1[i][j].equals(array2[i][j]))) { - return false; + String a = array1[i][j]; + String b = array2[i][j]; + + if (a == null || b == null) { + if (a != b) return false; // one null, one not -> false + } else { + boolean equal = usingRegex ? b.matches(a) : a.equals(b); + if (!equal) return false; } } } diff --git a/src/main/java/cz/jzitnik/game/entities/GameStates.java b/src/main/java/cz/jzitnik/game/entities/GameStates.java index e93ec68..9ff283b 100644 --- a/src/main/java/cz/jzitnik/game/entities/GameStates.java +++ b/src/main/java/cz/jzitnik/game/entities/GameStates.java @@ -1,11 +1,13 @@ package cz.jzitnik.game.entities; import cz.jzitnik.game.Game; +import cz.jzitnik.game.context.GlobalContextProvider; import cz.jzitnik.game.ui.CraftingTable; public class GameStates { public CraftingTable craftingTable; public Dependencies dependencies; + public GlobalContextProvider globalContextProvider = new GlobalContextProvider(); public int clickX = -1; public int clickY = -1; diff --git a/src/main/java/cz/jzitnik/game/entities/Player.java b/src/main/java/cz/jzitnik/game/entities/Player.java index 43d6db9..9881e40 100644 --- a/src/main/java/cz/jzitnik/game/entities/Player.java +++ b/src/main/java/cz/jzitnik/game/entities/Player.java @@ -41,6 +41,14 @@ public class Player { fallDistance++; } + public void resetHealth() { + health = 10; + } + + public void resetHunger() { + hunger = 10; + } + public void fell(List fallblock, Game game, ScreenRenderer screenRenderer) { var block = fallblock.stream().filter(b -> b.getClass().isAnnotationPresent(ReduceFallDamage.class)) .findFirst(); diff --git a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/work/BedBlock.java b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/work/BedBlock.java index a549559..ea3ba60 100644 --- a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/work/BedBlock.java +++ b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/work/BedBlock.java @@ -5,7 +5,8 @@ import cz.jzitnik.game.annotations.*; import cz.jzitnik.game.core.reducefalldamage.BedFallDamageReducer; import cz.jzitnik.game.core.sound.SoundKey; import cz.jzitnik.game.entities.Block; -import cz.jzitnik.game.sprites.Bed; +import static cz.jzitnik.game.sprites.Bed.BedState; +import cz.jzitnik.game.blocks.Bed; @MineSound(SoundKey.WOOD_DIG) @PlaceSound(SoundKey.WOOD_DIG) @@ -17,6 +18,7 @@ public class BedBlock extends Block { public BedBlock() { super("bed", SpriteLoader.SPRITES.BED, 2); setGhost(true); - setSpriteState(Bed.BedState.RIGHT); + setSpriteState(BedState.RIGHT); + setData(new Bed()); } } diff --git a/src/main/java/cz/jzitnik/game/entities/items/registry/items/ores/coal/CoalOreItem.java b/src/main/java/cz/jzitnik/game/entities/items/registry/items/ores/coal/CoalOreItem.java index 1aa437c..7c3d062 100644 --- a/src/main/java/cz/jzitnik/game/entities/items/registry/items/ores/coal/CoalOreItem.java +++ b/src/main/java/cz/jzitnik/game/entities/items/registry/items/ores/coal/CoalOreItem.java @@ -2,9 +2,11 @@ package cz.jzitnik.game.entities.items.registry.items.ores.coal; import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.annotations.ItemRegistry; +import cz.jzitnik.game.annotations.Smeltable; import cz.jzitnik.game.entities.items.Item; import cz.jzitnik.game.entities.items.ItemType; +@Smeltable("coal") @ItemRegistry("coal_ore") public class CoalOreItem extends Item { public CoalOreItem() { diff --git a/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieLogic.java b/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieLogic.java index e61067b..df223d0 100644 --- a/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieLogic.java +++ b/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieLogic.java @@ -34,12 +34,11 @@ public class ZombieLogic @Override public void spawn(int playerX, int playerY, Game game, Terminal terminal) { int[] view = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length); - var world = game.getWorld(); int startX = view[0]; int endX = view[1]; - attemptZombieSpawn(startX - 20, startX - 5, playerY, game); - attemptZombieSpawn(endX + 5, endX + 20, playerY, game); + //attemptZombieSpawn(startX - 20, startX - 5, playerY, game); + //attemptZombieSpawn(endX + 5, endX + 20, playerY, game); } private void attemptZombieSpawn(int startX, int endX, int centerY, Game game) { @@ -106,8 +105,8 @@ public class ZombieLogic @Override public void killed(Game game, Block mob) { int rottenFlesh = random.nextInt(3) + 1; - InventoryItem drop = new InventoryItem(rottenFlesh, ItemBlockSupplier.getItem("rotten_flesh")); - game.getInventory().addItem(drop); + //InventoryItem drop = new InventoryItem(rottenFlesh, ItemBlockSupplier.getItem("rotten_flesh")); + //game.getInventory().addItem(drop); } public static Map.Entry getRandomEntry(HashMap map) { diff --git a/src/main/java/cz/jzitnik/game/ui/DeathScreen.java b/src/main/java/cz/jzitnik/game/ui/DeathScreen.java index 4313614..f8b54b8 100644 --- a/src/main/java/cz/jzitnik/game/ui/DeathScreen.java +++ b/src/main/java/cz/jzitnik/game/ui/DeathScreen.java @@ -110,6 +110,9 @@ public class DeathScreen { if (buttony > top && buttony < bottom) { if (type == MouseEvent.Type.Pressed) { game.setWindow(Window.WORLD); + game.getPlayer().resetHealth(); + game.getPlayer().resetHunger(); + screenRenderer.render(game); return; } if (!buttonHover) { diff --git a/src/main/java/cz/jzitnik/tui/Sprite.java b/src/main/java/cz/jzitnik/tui/Sprite.java index 7ae58f0..5ad9af8 100644 --- a/src/main/java/cz/jzitnik/tui/Sprite.java +++ b/src/main/java/cz/jzitnik/tui/Sprite.java @@ -81,11 +81,7 @@ public abstract class Sprite> { protected final String getResource(E key) { var resource = resources.get(key); - if (resource.isEmpty()) { - return loadResource(key); - } else { - return resource.get(); - } + return resource.orElseGet(() -> loadResource(key)); } /**