From 940c3f3fe58d4895fdb76d3f55891b53f319d28f Mon Sep 17 00:00:00 2001 From: jzitnik-dev Date: Sat, 1 Mar 2025 17:29:30 +0100 Subject: [PATCH] feat: Implemented pig spawning and pig walking --- pom.xml | 6 + src/main/java/cz/jzitnik/Main.java | 5 +- src/main/java/cz/jzitnik/game/Game.java | 59 +++--- ...sterPlaceHandler.java => EntityLogic.java} | 4 +- .../jzitnik/game/annotations/EntitySpawn.java | 11 ++ .../game/annotations/PlaceHandler.java | 13 ++ .../game/annotations/RightClickLogic.java | 11 ++ .../cz/jzitnik/game/{ui => blocks}/Chest.java | 16 +- .../jzitnik/game/{ui => blocks}/Furnace.java | 20 +- .../cz/jzitnik/game/blocks/OakDoorData.java | 51 +++++ .../jzitnik/game/crafting/CraftingRecipe.java | 2 +- .../game/crafting/CraftingRecipeList.java | 4 +- .../cz/jzitnik/game/{ => entities}/Block.java | 10 +- .../game/{ => entities}/Dependencies.java | 2 +- .../game/{ => entities}/GameStates.java | 3 +- .../jzitnik/game/{ => entities}/Player.java | 2 +- .../{ => entities}/items/InventoryItem.java | 2 +- .../game/{ => entities}/items/Item.java | 4 +- .../items/ItemBlockSupplier.java | 23 ++- .../game/{ => entities}/items/ItemType.java | 2 +- .../{ => entities}/items/ToolVariant.java | 2 +- .../game/generation/CaveGenerator.java | 12 -- .../jzitnik/game/generation/Generation.java | 11 +- .../game/handlers/place/PlaceHandler.java | 5 +- .../place/handlers/DoorPlaceHandler.java | 48 +---- .../rightclick/RightClickHandler.java | 36 +--- .../rightclick/RightClickHandlerProvider.java | 29 +++ .../game/mobs/EntityLogicInterface.java | 5 + .../game/mobs/EntityLogicProvider.java | 82 ++++++++ .../game/mobs/EntitySpawnInterface.java | 8 + .../game/mobs/EntitySpawnProvider.java | 57 ++++++ .../game/mobs/services/pig/PigData.java | 12 ++ .../game/mobs/services/pig/PigLogic.java | 177 ++++++++++++++++++ .../cz/jzitnik/game/smelting/Smelting.java | 4 +- .../threads/HealthRegenerationThread.java | 2 +- .../game/threads/HungerDrainThread.java | 2 +- .../game/threads/InputHandlerThread.java | 6 +- .../cz/jzitnik/game/ui/CraftingTable.java | 2 +- .../java/cz/jzitnik/game/ui/Inventory.java | 4 +- .../game/ui/InventoryClickHandler.java | 2 +- .../java/cz/jzitnik/game/ui/InventoryDTO.java | 2 +- .../jzitnik/game/ui/SmallCraftingTable.java | 2 +- .../java/cz/jzitnik/tui/ScreenRenderer.java | 14 +- 43 files changed, 601 insertions(+), 173 deletions(-) rename src/main/java/cz/jzitnik/game/annotations/{RegisterPlaceHandler.java => EntityLogic.java} (87%) create mode 100644 src/main/java/cz/jzitnik/game/annotations/EntitySpawn.java create mode 100644 src/main/java/cz/jzitnik/game/annotations/PlaceHandler.java create mode 100644 src/main/java/cz/jzitnik/game/annotations/RightClickLogic.java rename src/main/java/cz/jzitnik/game/{ui => blocks}/Chest.java (87%) rename src/main/java/cz/jzitnik/game/{ui => blocks}/Furnace.java (94%) create mode 100644 src/main/java/cz/jzitnik/game/blocks/OakDoorData.java rename src/main/java/cz/jzitnik/game/{ => entities}/Block.java (90%) rename src/main/java/cz/jzitnik/game/{ => entities}/Dependencies.java (80%) rename src/main/java/cz/jzitnik/game/{ => entities}/GameStates.java (84%) rename src/main/java/cz/jzitnik/game/{ => entities}/Player.java (95%) rename src/main/java/cz/jzitnik/game/{ => entities}/items/InventoryItem.java (95%) rename src/main/java/cz/jzitnik/game/{ => entities}/items/Item.java (95%) rename src/main/java/cz/jzitnik/game/{ => entities}/items/ItemBlockSupplier.java (91%) rename src/main/java/cz/jzitnik/game/{ => entities}/items/ItemType.java (71%) rename src/main/java/cz/jzitnik/game/{ => entities}/items/ToolVariant.java (60%) delete mode 100644 src/main/java/cz/jzitnik/game/generation/CaveGenerator.java create mode 100644 src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandlerProvider.java create mode 100644 src/main/java/cz/jzitnik/game/mobs/EntityLogicInterface.java create mode 100644 src/main/java/cz/jzitnik/game/mobs/EntityLogicProvider.java create mode 100644 src/main/java/cz/jzitnik/game/mobs/EntitySpawnInterface.java create mode 100644 src/main/java/cz/jzitnik/game/mobs/EntitySpawnProvider.java create mode 100644 src/main/java/cz/jzitnik/game/mobs/services/pig/PigData.java create mode 100644 src/main/java/cz/jzitnik/game/mobs/services/pig/PigLogic.java diff --git a/pom.xml b/pom.xml index e9111aa..58aeff5 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,12 @@ guava 31.1-jre + + + com.fasterxml.jackson.core + jackson-databind + 2.18.2 + diff --git a/src/main/java/cz/jzitnik/Main.java b/src/main/java/cz/jzitnik/Main.java index b0e4f23..e933c7c 100644 --- a/src/main/java/cz/jzitnik/Main.java +++ b/src/main/java/cz/jzitnik/Main.java @@ -1,6 +1,7 @@ package cz.jzitnik; import cz.jzitnik.game.Game; +import cz.jzitnik.game.mobs.EntityLogicProvider; import cz.jzitnik.game.threads.HealthRegenerationThread; import cz.jzitnik.game.threads.HungerDrainThread; import cz.jzitnik.game.threads.InputHandlerThread; @@ -33,16 +34,18 @@ public class Main { Thread inputHandlerThread = new InputHandlerThread(game, terminal, screenRenderer, isRunning); Thread healingThread = new HealthRegenerationThread(game.getPlayer()); Thread hungerDrainThread = new HungerDrainThread(game.getPlayer()); + EntityLogicProvider entityLogicProvider = new EntityLogicProvider(); + // Start all threads healingThread.start(); hungerDrainThread.start(); inputHandlerThread.start(); - while (isRunning[0]) { if (game.getWindow() == Window.WORLD) { screenRenderer.render(game); } + entityLogicProvider.update(game); Thread.sleep(1000); } diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index aa028d3..adc380a 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -1,16 +1,21 @@ package cz.jzitnik.game; +import com.fasterxml.jackson.annotation.JsonIgnore; +import cz.jzitnik.game.entities.Block; +import cz.jzitnik.game.entities.GameStates; +import cz.jzitnik.game.entities.Player; import cz.jzitnik.game.generation.Generation; -import cz.jzitnik.game.items.Item; -import cz.jzitnik.game.items.ItemType; +import cz.jzitnik.game.entities.items.Item; +import cz.jzitnik.game.entities.items.ItemType; import cz.jzitnik.game.handlers.place.CustomPlaceHandler; +import cz.jzitnik.game.mobs.EntitySpawnProvider; import cz.jzitnik.game.sprites.Breaking; import cz.jzitnik.game.sprites.Steve; -import cz.jzitnik.game.ui.Chest; -import cz.jzitnik.game.ui.Furnace; +import cz.jzitnik.game.blocks.Chest; +import cz.jzitnik.game.blocks.Furnace; import cz.jzitnik.game.ui.Window; import cz.jzitnik.game.ui.Inventory; -import cz.jzitnik.game.handlers.rightclick.RightClickHandler; +import cz.jzitnik.game.handlers.rightclick.RightClickHandlerProvider; import cz.jzitnik.tui.ScreenMovingCalculationProvider; import cz.jzitnik.tui.ScreenRenderer; import lombok.Getter; @@ -28,12 +33,14 @@ public class Game { private boolean mining = false; @Setter private Window window = Window.WORLD; - - //jsonignore - private final GameStates gameStates = new GameStates(this); - private final Inventory inventory = new Inventory(); + @JsonIgnore + private final EntitySpawnProvider entitySpawnProvider = new EntitySpawnProvider(); + + @JsonIgnore + private final GameStates gameStates = new GameStates(this); + public Game() { Generation.generateWorld(this); } @@ -51,13 +58,13 @@ public class Game { return null; } - public void movePlayerRight(ScreenRenderer screenRenderer) { + public void movePlayerRight(ScreenRenderer screenRenderer, Terminal terminal) { if (window != Window.WORLD) { return; } int[] cords = getPlayerCords(); - if (world[cords[1]][cords[0] + 1].stream().anyMatch(block -> !block.isGhost()) || world[cords[1] - 1][cords[0] + 1].stream().anyMatch(block -> !block.isGhost())) { + if (isSolid(world[cords[1]][cords[0] + 1]) || isSolid(world[cords[1] - 1][cords[0] + 1])) { return; } @@ -67,16 +74,18 @@ public class Game { world[cords[1]-1][cords[0]].remove(player.getPlayerBlock1()); screenRenderer.render(this); + entitySpawnProvider.update(this, terminal); + update(screenRenderer); } - public void movePlayerLeft(ScreenRenderer screenRenderer) { + public void movePlayerLeft(ScreenRenderer screenRenderer, Terminal terminal) { if (window != Window.WORLD) { return; } int[] cords = getPlayerCords(); - if (world[cords[1]][cords[0] - 1].stream().anyMatch(block -> !block.isGhost()) || world[cords[1] - 1][cords[0] - 1].stream().anyMatch(block -> !block.isGhost())) { + if (isSolid(world[cords[1]][cords[0] - 1]) || isSolid(world[cords[1] - 1][cords[0] - 1])) { return; } @@ -86,6 +95,8 @@ public class Game { world[cords[1]-1][cords[0]].remove(player.getPlayerBlock1()); screenRenderer.render(this); + entitySpawnProvider.update(this, terminal); + update(screenRenderer); } @@ -95,7 +106,7 @@ public class Game { } int[] cords = getPlayerCords(); - if (world[cords[1] - 2][cords[0]].stream().anyMatch(block -> !block.isGhost()) || world[cords[1] + 1][cords[0]].stream().allMatch(Block::isGhost)) { + if (isSolid(world[cords[1] - 2][cords[0]]) || !isSolid(world[cords[1] + 1][cords[0]])) { return; } @@ -106,20 +117,12 @@ public class Game { new Thread(() -> { try { - Thread.sleep(500); + Thread.sleep(400); } catch (InterruptedException e) { throw new RuntimeException(e); } - int[] cords2 = getPlayerCords(); - if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) { - world[cords2[1] - 1][cords2[0]].remove(player.getPlayerBlock1()); - world[cords2[1]][cords2[0]].add(player.getPlayerBlock1()); - world[cords2[1] + 1][cords2[0]].add(player.getPlayerBlock2()); - world[cords2[1]][cords2[0]].remove(player.getPlayerBlock2()); - - screenRenderer.render(this); - } + update(screenRenderer); }).start(); } @@ -233,7 +236,7 @@ public class Game { } int[] cords2 = getPlayerCords(); - if (world[cords2[1] + 1][cords2[0]].stream().allMatch(Block::isGhost)) { + if (!isSolid(world[cords2[1] + 1][cords2[0]])) { world[cords2[1] - 1][cords2[0]].remove(player.getPlayerBlock1()); world[cords2[1]][cords2[0]].add(player.getPlayerBlock1()); world[cords2[1] + 1][cords2[0]].add(player.getPlayerBlock2()); @@ -268,7 +271,7 @@ public class Game { } if (!blocks.stream().allMatch(block -> block.getBlockId().equals("air"))) { - RightClickHandler.handle(x, y, this, screenRenderer); + RightClickHandlerProvider.handle(x, y, this, screenRenderer); screenRenderer.render(this); return; } @@ -293,4 +296,8 @@ public class Game { inventory.setItemInhHandIndex(slot); screenRenderer.render(this); } + + public boolean isSolid(List blocks) { + return !blocks.stream().allMatch(Block::isGhost); + } } \ No newline at end of file diff --git a/src/main/java/cz/jzitnik/game/annotations/RegisterPlaceHandler.java b/src/main/java/cz/jzitnik/game/annotations/EntityLogic.java similarity index 87% rename from src/main/java/cz/jzitnik/game/annotations/RegisterPlaceHandler.java rename to src/main/java/cz/jzitnik/game/annotations/EntityLogic.java index d9cc272..3775c60 100644 --- a/src/main/java/cz/jzitnik/game/annotations/RegisterPlaceHandler.java +++ b/src/main/java/cz/jzitnik/game/annotations/EntityLogic.java @@ -5,8 +5,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.ElementType; -@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) -public @interface RegisterPlaceHandler { +@Retention(RetentionPolicy.RUNTIME) +public @interface EntityLogic { String value(); } diff --git a/src/main/java/cz/jzitnik/game/annotations/EntitySpawn.java b/src/main/java/cz/jzitnik/game/annotations/EntitySpawn.java new file mode 100644 index 0000000..1d9c4d6 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/EntitySpawn.java @@ -0,0 +1,11 @@ +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; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface EntitySpawn { +} diff --git a/src/main/java/cz/jzitnik/game/annotations/PlaceHandler.java b/src/main/java/cz/jzitnik/game/annotations/PlaceHandler.java new file mode 100644 index 0000000..2c61b9f --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/PlaceHandler.java @@ -0,0 +1,13 @@ +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 PlaceHandler { + String value(); +} diff --git a/src/main/java/cz/jzitnik/game/annotations/RightClickLogic.java b/src/main/java/cz/jzitnik/game/annotations/RightClickLogic.java new file mode 100644 index 0000000..22cbaac --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/RightClickLogic.java @@ -0,0 +1,11 @@ +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; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface RightClickLogic { +} diff --git a/src/main/java/cz/jzitnik/game/ui/Chest.java b/src/main/java/cz/jzitnik/game/blocks/Chest.java similarity index 87% rename from src/main/java/cz/jzitnik/game/ui/Chest.java rename to src/main/java/cz/jzitnik/game/blocks/Chest.java index c21e01d..dfb2d7c 100644 --- a/src/main/java/cz/jzitnik/game/ui/Chest.java +++ b/src/main/java/cz/jzitnik/game/blocks/Chest.java @@ -1,7 +1,11 @@ -package cz.jzitnik.game.ui; +package cz.jzitnik.game.blocks; import cz.jzitnik.game.Game; -import cz.jzitnik.game.items.InventoryItem; +import cz.jzitnik.game.annotations.RightClickLogic; +import cz.jzitnik.game.entities.items.InventoryItem; +import cz.jzitnik.game.handlers.rightclick.RightClickHandler; +import cz.jzitnik.game.ui.InventoryClickHandler; +import cz.jzitnik.game.ui.Window; import cz.jzitnik.tui.ScreenRenderer; import cz.jzitnik.tui.SpriteList; import org.jline.terminal.MouseEvent; @@ -10,7 +14,8 @@ import org.jline.terminal.Terminal; import java.util.List; import java.util.Optional; -public class Chest { +@RightClickLogic +public class Chest implements RightClickHandler { private static final int ROW_AMOUNT = 4; private static final int COLUMN_AMOUNT = 6; private static final int CELL_WIDTH = 50; @@ -91,4 +96,9 @@ public class Chest { items[i] = null; } } + + @Override + public void onBlockRightClick(int ignored, int ignored2, Game game, ScreenRenderer ignored4) { + game.setWindow(Window.CHEST); + } } diff --git a/src/main/java/cz/jzitnik/game/ui/Furnace.java b/src/main/java/cz/jzitnik/game/blocks/Furnace.java similarity index 94% rename from src/main/java/cz/jzitnik/game/ui/Furnace.java rename to src/main/java/cz/jzitnik/game/blocks/Furnace.java index b218fe5..8d941be 100644 --- a/src/main/java/cz/jzitnik/game/ui/Furnace.java +++ b/src/main/java/cz/jzitnik/game/blocks/Furnace.java @@ -1,10 +1,14 @@ -package cz.jzitnik.game.ui; +package cz.jzitnik.game.blocks; -import cz.jzitnik.game.Block; +import cz.jzitnik.game.annotations.RightClickLogic; +import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.Game; -import cz.jzitnik.game.items.InventoryItem; -import cz.jzitnik.game.items.Item; +import cz.jzitnik.game.entities.items.InventoryItem; +import cz.jzitnik.game.entities.items.Item; +import cz.jzitnik.game.handlers.rightclick.RightClickHandler; import cz.jzitnik.game.smelting.Smelting; +import cz.jzitnik.game.ui.InventoryClickHandler; +import cz.jzitnik.game.ui.Window; import cz.jzitnik.tui.ScreenRenderer; import cz.jzitnik.tui.SpriteList; import cz.jzitnik.tui.utils.Numbers; @@ -15,7 +19,8 @@ import org.jline.terminal.Terminal; import java.util.List; import java.util.Optional; -public class Furnace { +@RightClickLogic +public class Furnace implements RightClickHandler { private final Block block; private final InventoryItem[] items = new InventoryItem[2]; private InventoryItem outputItem; @@ -254,4 +259,9 @@ public class Furnace { setSmelting(false); } + + @Override + public void onBlockRightClick(int ignored, int ignored2, Game game, ScreenRenderer ignored3) { + game.setWindow(Window.FURNACE); + } } diff --git a/src/main/java/cz/jzitnik/game/blocks/OakDoorData.java b/src/main/java/cz/jzitnik/game/blocks/OakDoorData.java new file mode 100644 index 0000000..d9683d5 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/blocks/OakDoorData.java @@ -0,0 +1,51 @@ +package cz.jzitnik.game.blocks; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.RightClickLogic; +import cz.jzitnik.game.entities.Block; +import cz.jzitnik.game.handlers.rightclick.RightClickHandler; +import cz.jzitnik.game.sprites.OakDoor; + +import cz.jzitnik.tui.ScreenRenderer; + +@RightClickLogic +public class OakDoorData implements RightClickHandler { + private 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()); + }); + } + + public void onBlockRightClick(int x, int y, Game game, 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); + } +} diff --git a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipe.java b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipe.java index f74c4ad..39d138f 100644 --- a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipe.java +++ b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipe.java @@ -1,6 +1,6 @@ package cz.jzitnik.game.crafting; -import cz.jzitnik.game.items.InventoryItem; +import cz.jzitnik.game.entities.items.InventoryItem; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java index f03a109..6c3c280 100644 --- a/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java +++ b/src/main/java/cz/jzitnik/game/crafting/CraftingRecipeList.java @@ -1,7 +1,7 @@ package cz.jzitnik.game.crafting; -import cz.jzitnik.game.items.InventoryItem; -import cz.jzitnik.game.items.ItemBlockSupplier; +import cz.jzitnik.game.entities.items.InventoryItem; +import cz.jzitnik.game.entities.items.ItemBlockSupplier; import java.util.*; diff --git a/src/main/java/cz/jzitnik/game/Block.java b/src/main/java/cz/jzitnik/game/entities/Block.java similarity index 90% rename from src/main/java/cz/jzitnik/game/Block.java rename to src/main/java/cz/jzitnik/game/entities/Block.java index 713f5ba..e655840 100644 --- a/src/main/java/cz/jzitnik/game/Block.java +++ b/src/main/java/cz/jzitnik/game/entities/Block.java @@ -1,8 +1,9 @@ -package cz.jzitnik.game; +package cz.jzitnik.game.entities; -import cz.jzitnik.game.items.Item; -import cz.jzitnik.game.items.ItemType; -import cz.jzitnik.game.items.ToolVariant; +import cz.jzitnik.game.SpriteLoader; +import cz.jzitnik.game.entities.items.Item; +import cz.jzitnik.game.entities.items.ItemType; +import cz.jzitnik.game.entities.items.ToolVariant; import cz.jzitnik.game.ui.Inventory; import lombok.Getter; import lombok.Setter; @@ -24,6 +25,7 @@ public class Block { private List toolVariants = new ArrayList<>(); private List drops = new ArrayList<>(); private Object data = null; + private boolean isMob = false; public Block(String blockId, SpriteLoader.SPRITES sprite) { this.blockId = blockId; diff --git a/src/main/java/cz/jzitnik/game/Dependencies.java b/src/main/java/cz/jzitnik/game/entities/Dependencies.java similarity index 80% rename from src/main/java/cz/jzitnik/game/Dependencies.java rename to src/main/java/cz/jzitnik/game/entities/Dependencies.java index 62e6837..c0a34ad 100644 --- a/src/main/java/cz/jzitnik/game/Dependencies.java +++ b/src/main/java/cz/jzitnik/game/entities/Dependencies.java @@ -1,4 +1,4 @@ -package cz.jzitnik.game; +package cz.jzitnik.game.entities; import cz.jzitnik.game.handlers.place.PlaceHandler; diff --git a/src/main/java/cz/jzitnik/game/GameStates.java b/src/main/java/cz/jzitnik/game/entities/GameStates.java similarity index 84% rename from src/main/java/cz/jzitnik/game/GameStates.java rename to src/main/java/cz/jzitnik/game/entities/GameStates.java index b10d2b1..c3367da 100644 --- a/src/main/java/cz/jzitnik/game/GameStates.java +++ b/src/main/java/cz/jzitnik/game/entities/GameStates.java @@ -1,5 +1,6 @@ -package cz.jzitnik.game; +package cz.jzitnik.game.entities; +import cz.jzitnik.game.Game; import cz.jzitnik.game.ui.CraftingTable; public class GameStates { diff --git a/src/main/java/cz/jzitnik/game/Player.java b/src/main/java/cz/jzitnik/game/entities/Player.java similarity index 95% rename from src/main/java/cz/jzitnik/game/Player.java rename to src/main/java/cz/jzitnik/game/entities/Player.java index 7958b95..5a61710 100644 --- a/src/main/java/cz/jzitnik/game/Player.java +++ b/src/main/java/cz/jzitnik/game/entities/Player.java @@ -1,4 +1,4 @@ -package cz.jzitnik.game; +package cz.jzitnik.game.entities; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/cz/jzitnik/game/items/InventoryItem.java b/src/main/java/cz/jzitnik/game/entities/items/InventoryItem.java similarity index 95% rename from src/main/java/cz/jzitnik/game/items/InventoryItem.java rename to src/main/java/cz/jzitnik/game/entities/items/InventoryItem.java index 80fe605..44d59fa 100644 --- a/src/main/java/cz/jzitnik/game/items/InventoryItem.java +++ b/src/main/java/cz/jzitnik/game/entities/items/InventoryItem.java @@ -1,4 +1,4 @@ -package cz.jzitnik.game.items; +package cz.jzitnik.game.entities.items; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/cz/jzitnik/game/items/Item.java b/src/main/java/cz/jzitnik/game/entities/items/Item.java similarity index 95% rename from src/main/java/cz/jzitnik/game/items/Item.java rename to src/main/java/cz/jzitnik/game/entities/items/Item.java index 8cb9834..9fe1f50 100644 --- a/src/main/java/cz/jzitnik/game/items/Item.java +++ b/src/main/java/cz/jzitnik/game/entities/items/Item.java @@ -1,6 +1,6 @@ -package cz.jzitnik.game.items; +package cz.jzitnik.game.entities.items; -import cz.jzitnik.game.Block; +import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.SpriteLoader; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java b/src/main/java/cz/jzitnik/game/entities/items/ItemBlockSupplier.java similarity index 91% rename from src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java rename to src/main/java/cz/jzitnik/game/entities/items/ItemBlockSupplier.java index b03aa33..6f82d46 100644 --- a/src/main/java/cz/jzitnik/game/items/ItemBlockSupplier.java +++ b/src/main/java/cz/jzitnik/game/entities/items/ItemBlockSupplier.java @@ -1,9 +1,11 @@ -package cz.jzitnik.game.items; +package cz.jzitnik.game.entities.items; import cz.jzitnik.game.SpriteLoader; -import cz.jzitnik.game.Block; -import cz.jzitnik.game.ui.Chest; -import cz.jzitnik.game.ui.Furnace; +import cz.jzitnik.game.blocks.OakDoorData; +import cz.jzitnik.game.entities.Block; +import cz.jzitnik.game.blocks.Chest; +import cz.jzitnik.game.blocks.Furnace; +import cz.jzitnik.game.mobs.services.pig.PigData; import java.util.ArrayList; import java.util.Arrays; @@ -93,6 +95,7 @@ public class ItemBlockSupplier { } public static Block oakDoor() { var block = new Block("oak_door", SpriteLoader.SPRITES.OAK_DOOR, 3, ItemType.AXE, new ArrayList<>()); + block.setData(new OakDoorData()); block.setDrops(List.of(Helper.oakDoor(block))); return block; } @@ -146,4 +149,16 @@ public class ItemBlockSupplier { return Helper.oakDoor(Blocks.oakDoor()); } } + + public static class Mobs { + public static Block pig() { + // Yes, pig is a block. Cry about it. + var block = new Block("pig", SpriteLoader.SPRITES.PIG); + block.setMob(true); + block.setGhost(true); + block.setMineable(false); + block.setData(new PigData()); + return block; + } + } } diff --git a/src/main/java/cz/jzitnik/game/items/ItemType.java b/src/main/java/cz/jzitnik/game/entities/items/ItemType.java similarity index 71% rename from src/main/java/cz/jzitnik/game/items/ItemType.java rename to src/main/java/cz/jzitnik/game/entities/items/ItemType.java index 007de75..d8cde61 100644 --- a/src/main/java/cz/jzitnik/game/items/ItemType.java +++ b/src/main/java/cz/jzitnik/game/entities/items/ItemType.java @@ -1,4 +1,4 @@ -package cz.jzitnik.game.items; +package cz.jzitnik.game.entities.items; public enum ItemType { PICKAXE, diff --git a/src/main/java/cz/jzitnik/game/items/ToolVariant.java b/src/main/java/cz/jzitnik/game/entities/items/ToolVariant.java similarity index 60% rename from src/main/java/cz/jzitnik/game/items/ToolVariant.java rename to src/main/java/cz/jzitnik/game/entities/items/ToolVariant.java index 8ee5c8b..6a9edf7 100644 --- a/src/main/java/cz/jzitnik/game/items/ToolVariant.java +++ b/src/main/java/cz/jzitnik/game/entities/items/ToolVariant.java @@ -1,4 +1,4 @@ -package cz.jzitnik.game.items; +package cz.jzitnik.game.entities.items; public enum ToolVariant { WOODEN, diff --git a/src/main/java/cz/jzitnik/game/generation/CaveGenerator.java b/src/main/java/cz/jzitnik/game/generation/CaveGenerator.java deleted file mode 100644 index a27c98f..0000000 --- a/src/main/java/cz/jzitnik/game/generation/CaveGenerator.java +++ /dev/null @@ -1,12 +0,0 @@ -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[][] world, int[] terrainHeight) { - } -} diff --git a/src/main/java/cz/jzitnik/game/generation/Generation.java b/src/main/java/cz/jzitnik/game/generation/Generation.java index 7798e79..965b77a 100644 --- a/src/main/java/cz/jzitnik/game/generation/Generation.java +++ b/src/main/java/cz/jzitnik/game/generation/Generation.java @@ -1,10 +1,10 @@ package cz.jzitnik.game.generation; -import cz.jzitnik.game.Block; +import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.Game; import cz.jzitnik.game.SpriteLoader; -import cz.jzitnik.game.items.ItemBlockSupplier; -import cz.jzitnik.game.items.ItemType; +import cz.jzitnik.game.entities.items.ItemBlockSupplier; +import cz.jzitnik.game.entities.items.ItemType; import cz.jzitnik.game.sprites.Steve; import java.util.ArrayList; @@ -18,13 +18,16 @@ public class Generation { Block steveBlock = new Block("steve", SpriteLoader.SPRITES.STEVE); steveBlock.setSpriteState(Steve.SteveState.FIRST); + steveBlock.setGhost(true); Block steveBlock2 = new Block("steve", SpriteLoader.SPRITES.STEVE); steveBlock2.setSpriteState(Steve.SteveState.SECOND); + steveBlock2.setGhost(true); + + int[] terrainHeight = generateTerrain(); game.getPlayer().setPlayerBlock1(steveBlock); game.getPlayer().setPlayerBlock2(steveBlock2); - int[] terrainHeight = generateTerrain(); populateWorld(world, terrainHeight); plantTrees(world, terrainHeight); diff --git a/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java b/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java index 6c5d650..88db7d9 100644 --- a/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java +++ b/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java @@ -3,7 +3,6 @@ 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 { @@ -28,13 +27,13 @@ public class PlaceHandler { private void registerHandlers() { Reflections reflections = new Reflections("cz.jzitnik.game.handlers.place.handlers"); - Set> handlerClasses = reflections.getTypesAnnotatedWith(RegisterPlaceHandler.class); + Set> handlerClasses = reflections.getTypesAnnotatedWith(cz.jzitnik.game.annotations.PlaceHandler.class); for (Class clazz : handlerClasses) { if (CustomPlaceHandler.class.isAssignableFrom(clazz)) { try { CustomPlaceHandler handlerInstance = (CustomPlaceHandler) clazz.getDeclaredConstructor().newInstance(); - RegisterPlaceHandler annotation = clazz.getAnnotation(RegisterPlaceHandler.class); + cz.jzitnik.game.annotations.PlaceHandler annotation = clazz.getAnnotation(cz.jzitnik.game.annotations.PlaceHandler.class); placeHandlerList.put(annotation.value(), handlerInstance); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/cz/jzitnik/game/handlers/place/handlers/DoorPlaceHandler.java b/src/main/java/cz/jzitnik/game/handlers/place/handlers/DoorPlaceHandler.java index cd1672a..298ac8c 100644 --- a/src/main/java/cz/jzitnik/game/handlers/place/handlers/DoorPlaceHandler.java +++ b/src/main/java/cz/jzitnik/game/handlers/place/handlers/DoorPlaceHandler.java @@ -1,54 +1,14 @@ package cz.jzitnik.game.handlers.place.handlers; -import cz.jzitnik.game.Block; +import cz.jzitnik.game.annotations.PlaceHandler; +import cz.jzitnik.game.entities.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.entities.items.ItemBlockSupplier; import cz.jzitnik.game.sprites.OakDoor; -import cz.jzitnik.tui.ScreenRenderer; -@RegisterPlaceHandler("oak_door") +@PlaceHandler("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]; diff --git a/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandler.java b/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandler.java index 557f722..8769690 100644 --- a/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandler.java +++ b/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandler.java @@ -1,40 +1,8 @@ package cz.jzitnik.game.handlers.rightclick; -import cz.jzitnik.game.Block; import cz.jzitnik.game.Game; -import cz.jzitnik.game.handlers.place.handlers.DoorPlaceHandler; -import cz.jzitnik.game.ui.Window; import cz.jzitnik.tui.ScreenRenderer; -import java.util.HashMap; - -public class RightClickHandler { - @FunctionalInterface - public interface Function3 { - void apply(T t, U u); - } - - public static void handle(int x, int y, Game game, ScreenRenderer screenRenderer) { - if (game.isMining()) { - return; - } - - HashMap> functionMap = new HashMap<>(); - functionMap.put("crafting_table", game.getGameStates().craftingTable::render); - functionMap.put("chest", (Integer ignored, Integer ignored2) -> game.setWindow(Window.CHEST)); - 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().clickY = y; - - var blocks = game.getWorld()[y][x]; - for (Block block : blocks) { - if (functionMap.containsKey(block.getBlockId())) { - functionMap.get(block.getBlockId()).apply(x, y); - - return; - } - } - } +public interface RightClickHandler { + void onBlockRightClick(int x, int y, Game game, ScreenRenderer screenRenderer); } diff --git a/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandlerProvider.java b/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandlerProvider.java new file mode 100644 index 0000000..2fe0f55 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/handlers/rightclick/RightClickHandlerProvider.java @@ -0,0 +1,29 @@ +package cz.jzitnik.game.handlers.rightclick; + +import cz.jzitnik.game.annotations.RightClickLogic; +import cz.jzitnik.game.entities.Block; +import cz.jzitnik.game.Game; +import cz.jzitnik.tui.ScreenRenderer; + +public class RightClickHandlerProvider { + public static void handle(int x, int y, Game game, ScreenRenderer screenRenderer) { + if (game.isMining()) { + return; + } + + game.getGameStates().clickX = x; + game.getGameStates().clickY = y; + + var blocks = game.getWorld()[y][x]; + for (Block block : blocks) { + if (block.getBlockId().equalsIgnoreCase("crafting_table")) { + game.getGameStates().craftingTable.render(x, y); + continue; + } + + if (block.getData() != null && block.getData().getClass().isAnnotationPresent(RightClickLogic.class) && block.getData() instanceof RightClickHandler handlerClass) { + handlerClass.onBlockRightClick(x, y, game, screenRenderer); + } + } + } +} diff --git a/src/main/java/cz/jzitnik/game/mobs/EntityLogicInterface.java b/src/main/java/cz/jzitnik/game/mobs/EntityLogicInterface.java new file mode 100644 index 0000000..2ee5597 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/mobs/EntityLogicInterface.java @@ -0,0 +1,5 @@ +package cz.jzitnik.game.mobs; + +public interface EntityLogicInterface { + void nextIteration(EntityLogicProvider.EntityLogicMobDTO entityLogicMobDTO); +} diff --git a/src/main/java/cz/jzitnik/game/mobs/EntityLogicProvider.java b/src/main/java/cz/jzitnik/game/mobs/EntityLogicProvider.java new file mode 100644 index 0000000..3c334cc --- /dev/null +++ b/src/main/java/cz/jzitnik/game/mobs/EntityLogicProvider.java @@ -0,0 +1,82 @@ +package cz.jzitnik.game.mobs; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.EntityLogic; +import cz.jzitnik.game.entities.Block; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.reflections.Reflections; + +public class EntityLogicProvider { + private static final int CHUNK_SIZE = 20; + private final HashMap logicList = new HashMap<>(); + + @AllArgsConstructor + @Getter + public static class EntityLogicMobDTO { + private Game game; + private Block mob; + private int x; + private int y; + } + + public void update(Game game) { + int[] playerLocation = game.getPlayerCords(); + int playerX = playerLocation[0]; + int playerY = playerLocation[1]; + + int startX = Math.max(0, playerX - CHUNK_SIZE); + int startY = Math.max(0, playerY - CHUNK_SIZE); + int endX = Math.min(game.getWorld()[0].length, playerX + CHUNK_SIZE); + int endY = Math.min(game.getWorld().length, playerY + CHUNK_SIZE); + + List mobs = new ArrayList<>(); + + for (int y = startY; y <= endY; y++) { + for (int x = startX; x <= endX; x++) { + int finalX = x; + int finalY = y; + List blockMobs = game.getWorld()[y][x].stream().filter(Block::isMob).map(mob -> new EntityLogicMobDTO(game, mob, finalX, finalY)).toList(); + mobs.addAll(blockMobs); + } + } + + for (EntityLogicMobDTO entityLogicMobDTO : mobs) { + run(entityLogicMobDTO); + } + } + + private void run(EntityLogicMobDTO entityLogicMobDTO) { + if (!logicList.containsKey(entityLogicMobDTO.getMob().getBlockId())) { + return; + } + + logicList.get(entityLogicMobDTO.getMob().getBlockId()).nextIteration(entityLogicMobDTO); + } + + public EntityLogicProvider() { + registerHandlers(); + } + + private void registerHandlers() { + Reflections reflections = new Reflections("cz.jzitnik.game.mobs.services"); + Set> handlerClasses = reflections.getTypesAnnotatedWith(EntityLogic.class); + + for (Class clazz : handlerClasses) { + if (EntityLogicInterface.class.isAssignableFrom(clazz)) { + try { + EntityLogicInterface instance = (EntityLogicInterface) clazz.getDeclaredConstructor().newInstance(); + EntityLogic annotation = clazz.getAnnotation(EntityLogic.class); + logicList.put(annotation.value(), instance); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/cz/jzitnik/game/mobs/EntitySpawnInterface.java b/src/main/java/cz/jzitnik/game/mobs/EntitySpawnInterface.java new file mode 100644 index 0000000..0b6b003 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/mobs/EntitySpawnInterface.java @@ -0,0 +1,8 @@ +package cz.jzitnik.game.mobs; + +import cz.jzitnik.game.Game; +import org.jline.terminal.Terminal; + +public interface EntitySpawnInterface { + void spawn(int playerX, int playerY, Game game, Terminal terminal); +} diff --git a/src/main/java/cz/jzitnik/game/mobs/EntitySpawnProvider.java b/src/main/java/cz/jzitnik/game/mobs/EntitySpawnProvider.java new file mode 100644 index 0000000..9b00369 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/mobs/EntitySpawnProvider.java @@ -0,0 +1,57 @@ +package cz.jzitnik.game.mobs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.EntitySpawn; +import cz.jzitnik.game.entities.Block; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.jline.terminal.Terminal; +import org.reflections.Reflections; + +public class EntitySpawnProvider { + private final List spawnList = new ArrayList<>(); + + @AllArgsConstructor + @Getter + public static class EntityLogicMobDTO { + private Game game; + private Block mob; + private int x; + private int y; + } + + public void update(Game game, Terminal terminal) { + int[] playerLocation = game.getPlayerCords(); + int playerX = playerLocation[0]; + int playerY = playerLocation[1]; + + for (EntitySpawnInterface entitySpawnInterface: spawnList) { + + entitySpawnInterface.spawn(playerX, playerY, game, terminal); + } + } + + public EntitySpawnProvider() { + registerHandlers(); + } + + private void registerHandlers() { + Reflections reflections = new Reflections("cz.jzitnik.game.mobs.services"); + Set> handlerClasses = reflections.getTypesAnnotatedWith(EntitySpawn.class); + + for (Class clazz : handlerClasses) { + if (EntitySpawnInterface.class.isAssignableFrom(clazz)) { + try { + EntitySpawnInterface instance = (EntitySpawnInterface) clazz.getDeclaredConstructor().newInstance(); + spawnList.add(instance); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/cz/jzitnik/game/mobs/services/pig/PigData.java b/src/main/java/cz/jzitnik/game/mobs/services/pig/PigData.java new file mode 100644 index 0000000..8f95ab7 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/mobs/services/pig/PigData.java @@ -0,0 +1,12 @@ +package cz.jzitnik.game.mobs.services.pig; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class PigData { + private int lastDirection = 1; // 1 = right, -1 = left + private int movementCooldown = 0; + private int jumpAttempts = 0; +} diff --git a/src/main/java/cz/jzitnik/game/mobs/services/pig/PigLogic.java b/src/main/java/cz/jzitnik/game/mobs/services/pig/PigLogic.java new file mode 100644 index 0000000..4eaf70e --- /dev/null +++ b/src/main/java/cz/jzitnik/game/mobs/services/pig/PigLogic.java @@ -0,0 +1,177 @@ +package cz.jzitnik.game.mobs.services.pig; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.EntityLogic; +import cz.jzitnik.game.annotations.EntitySpawn; +import cz.jzitnik.game.entities.Block; +import cz.jzitnik.game.entities.items.ItemBlockSupplier; +import cz.jzitnik.game.mobs.EntityLogicInterface; +import cz.jzitnik.game.mobs.EntityLogicProvider; +import cz.jzitnik.game.mobs.EntitySpawnInterface; +import cz.jzitnik.game.sprites.Pig; +import cz.jzitnik.tui.ScreenMovingCalculationProvider; +import org.jline.terminal.Terminal; + +import java.util.*; + +@EntitySpawn +@EntityLogic("pig") +public class PigLogic implements EntityLogicInterface, EntitySpawnInterface { + private final Random random = new Random(); + + @Override + public void nextIteration(EntityLogicProvider.EntityLogicMobDTO entityLogicMobDTO) { + int pigX = entityLogicMobDTO.getX(); + int pigY = entityLogicMobDTO.getY(); + var game = entityLogicMobDTO.getGame(); + var pig = entityLogicMobDTO.getMob(); + var world = game.getWorld(); + var pigData = (PigData) pig.getData(); + + boolean updated = false; + int newPigX = pigX; + int newPigY = pigY; + + // Reduce movement cooldown + if (pigData.getMovementCooldown() > 0) { + pigData.setMovementCooldown(pigData.getMovementCooldown() - 1); + return; // Skip movement this iteration + } + + // Determine movement direction + int direction = pigData.getLastDirection(); + if (random.nextInt(10) < 3) { // 30% chance to change direction + direction = -direction; + } + pigData.setLastDirection(direction); + + // Update sprite direction + if (direction == 1) { + pig.setSpriteState(Pig.PigState.RIGHT); + } else { + pig.setSpriteState(Pig.PigState.LEFT); + } + + List blocksAhead = world[pigY][pigX + direction]; + if (!game.isSolid(blocksAhead)) { + world[pigY][pigX].remove(pig); + world[pigY][pigX + direction].add(pig); + newPigX = pigX + direction; + updated = true; + pigData.setJumpAttempts(0); // Reset jump attempts when moving forward + } else { + List blocksAboveAhead = world[pigY - 1][pigX + direction]; + List blocksTwoAboveAhead = world[pigY - 2][pigX + direction]; + + // Jump if there is only one block height obstacle and limit jump attempts + if (!game.isSolid(blocksAboveAhead) && game.isSolid(blocksAhead) && !game.isSolid(blocksTwoAboveAhead)) { + if (pigData.getJumpAttempts() < 2) { // Limit jumping attempts to prevent infinite jumping + world[pigY][pigX].remove(pig); + world[pigY - 1][pigX + direction].add(pig); + newPigX = pigX + direction; + newPigY = pigY - 1; + updated = true; + pigData.setJumpAttempts(pigData.getJumpAttempts() + 1); + } + } + } + + // Falling logic (avoid long falls) + while (updated) { + if (!game.isSolid(world[newPigY + 1][newPigX])) { + if (newPigY - pigY < 3) { // Only fall if it's at most 2 blocks drop + world[newPigY][newPigX].remove(pig); + world[newPigY + 1][newPigX].add(pig); + newPigY++; + } else { + updated = false; + } + } else { + updated = false; + } + } + + // Apply movement cooldown to slow down movement + pigData.setMovementCooldown(random.nextInt(3) + 1); // 1-3 iterations cooldown + } + + @Override + public void spawn(int playerX, int playerY, Game game, Terminal terminal) { + // Cordinates where player can see + int[] data = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length); + var world = game.getWorld(); + int startX = data[0]; + int endX = data[1]; + + // Left side + int lstartX = startX - 20; + int lendX = startX - 5; + int lstartY = playerY - 15; + int lendY = playerY + 15; + + if (countPrasata(lstartX, lendX, lstartY, lendY, game) < 3 && random.nextInt(100) < 2) { + var spawnLocations = pigCanSpawn(lstartX, lendX, playerY, game); + if (!spawnLocations.isEmpty()) { + System.out.println(spawnLocations.size()); + for (int i = 0; i < Math.min(4, spawnLocations.size()); i++) { + var randomLocation = getRandomEntry(spawnLocations); + int x = randomLocation.getKey(); + int y = randomLocation.getValue(); + + world[y][x].add(ItemBlockSupplier.Mobs.pig()); + } + } + } + + // Right side + int rstartX = endX + 5; + int rendX = endX + 20; + int rstartY = playerY - 15; + int rendY = playerY + 15; + + if (countPrasata(rstartX, rendX, rstartY, rendY, game) < 3 && random.nextInt(100) < 2) { + var spawnLocations = pigCanSpawn(rstartX, rendX, playerY, game); + if (!spawnLocations.isEmpty()) { + System.out.println(spawnLocations.size()); + for (int i = 0; i < Math.min(4, spawnLocations.size()); i++) { + var randomLocation = getRandomEntry(spawnLocations); + int x = randomLocation.getKey(); + int y = randomLocation.getValue(); + + world[y][x].add(ItemBlockSupplier.Mobs.pig()); + } + } + } + } + + public static Map.Entry getRandomEntry(HashMap map) { + List> entryList = new ArrayList<>(map.entrySet()); + Random random = new Random(); + return entryList.get(random.nextInt(entryList.size())); + } + + private HashMap pigCanSpawn(int startX, int endX, int playerY, Game game) { + var map = new HashMap(); + var world = game.getWorld(); + for (int x = startX; x <= endX; x++) { + for (int y = Math.max(0, playerY - 30); y < Math.min(world.length, playerY + 30); y++) { + if (world[y][x].stream().anyMatch(i -> i.getBlockId().equals("grass"))) { + map.put(x, y - 1); + } + } + } + + return map; + } + + private long countPrasata(int startX, int endX, int startY, int endY, Game game) { + long pigAmount = 0; + for (int y = startY; y <= endY; y++) { + for (int x = startX; x <= endX; x++) { + pigAmount += game.getWorld()[y][x].stream().filter(i -> i.getBlockId().equals("pig")).count(); + } + } + + return pigAmount; + } +} diff --git a/src/main/java/cz/jzitnik/game/smelting/Smelting.java b/src/main/java/cz/jzitnik/game/smelting/Smelting.java index 8aac464..2f06f63 100644 --- a/src/main/java/cz/jzitnik/game/smelting/Smelting.java +++ b/src/main/java/cz/jzitnik/game/smelting/Smelting.java @@ -1,7 +1,7 @@ package cz.jzitnik.game.smelting; -import cz.jzitnik.game.items.Item; -import cz.jzitnik.game.items.ItemBlockSupplier; +import cz.jzitnik.game.entities.items.Item; +import cz.jzitnik.game.entities.items.ItemBlockSupplier; import java.util.HashMap; import java.util.function.Supplier; diff --git a/src/main/java/cz/jzitnik/game/threads/HealthRegenerationThread.java b/src/main/java/cz/jzitnik/game/threads/HealthRegenerationThread.java index 0a9bded..451b523 100644 --- a/src/main/java/cz/jzitnik/game/threads/HealthRegenerationThread.java +++ b/src/main/java/cz/jzitnik/game/threads/HealthRegenerationThread.java @@ -1,6 +1,6 @@ package cz.jzitnik.game.threads; -import cz.jzitnik.game.Player; +import cz.jzitnik.game.entities.Player; import lombok.AllArgsConstructor; @AllArgsConstructor diff --git a/src/main/java/cz/jzitnik/game/threads/HungerDrainThread.java b/src/main/java/cz/jzitnik/game/threads/HungerDrainThread.java index dc0bc11..397f44a 100644 --- a/src/main/java/cz/jzitnik/game/threads/HungerDrainThread.java +++ b/src/main/java/cz/jzitnik/game/threads/HungerDrainThread.java @@ -1,6 +1,6 @@ package cz.jzitnik.game.threads; -import cz.jzitnik.game.Player; +import cz.jzitnik.game.entities.Player; import lombok.AllArgsConstructor; @AllArgsConstructor diff --git a/src/main/java/cz/jzitnik/game/threads/InputHandlerThread.java b/src/main/java/cz/jzitnik/game/threads/InputHandlerThread.java index d5f1e96..e077c83 100644 --- a/src/main/java/cz/jzitnik/game/threads/InputHandlerThread.java +++ b/src/main/java/cz/jzitnik/game/threads/InputHandlerThread.java @@ -1,6 +1,8 @@ package cz.jzitnik.game.threads; import cz.jzitnik.game.Game; +import cz.jzitnik.game.blocks.Chest; +import cz.jzitnik.game.blocks.Furnace; import cz.jzitnik.game.ui.*; import cz.jzitnik.tui.MouseHandler; import cz.jzitnik.tui.ScreenRenderer; @@ -54,11 +56,11 @@ public class InputHandlerThread extends Thread { switch (key) { case '1', '2', '3', '4', '5', '6', '7', '8', '9' -> game.changeSlot(key - 49, screenRenderer); case 'a' -> { - game.movePlayerLeft(screenRenderer); + game.movePlayerLeft(screenRenderer, terminal); screenRenderer.render(game); } case 'd' -> { - game.movePlayerRight(screenRenderer); + game.movePlayerRight(screenRenderer, terminal); screenRenderer.render(game); } case ' ' -> { diff --git a/src/main/java/cz/jzitnik/game/ui/CraftingTable.java b/src/main/java/cz/jzitnik/game/ui/CraftingTable.java index 09e0179..ce9e6d6 100644 --- a/src/main/java/cz/jzitnik/game/ui/CraftingTable.java +++ b/src/main/java/cz/jzitnik/game/ui/CraftingTable.java @@ -3,7 +3,7 @@ package cz.jzitnik.game.ui; import cz.jzitnik.game.Game; import cz.jzitnik.game.crafting.CraftingRecipe; import cz.jzitnik.game.crafting.CraftingRecipeList; -import cz.jzitnik.game.items.InventoryItem; +import cz.jzitnik.game.entities.items.InventoryItem; import cz.jzitnik.tui.ScreenRenderer; import cz.jzitnik.tui.utils.SpriteCombiner; import cz.jzitnik.tui.SpriteList; diff --git a/src/main/java/cz/jzitnik/game/ui/Inventory.java b/src/main/java/cz/jzitnik/game/ui/Inventory.java index 76f7477..d33cc45 100644 --- a/src/main/java/cz/jzitnik/game/ui/Inventory.java +++ b/src/main/java/cz/jzitnik/game/ui/Inventory.java @@ -1,7 +1,7 @@ package cz.jzitnik.game.ui; -import cz.jzitnik.game.items.InventoryItem; -import cz.jzitnik.game.items.Item; +import cz.jzitnik.game.entities.items.InventoryItem; +import cz.jzitnik.game.entities.items.Item; import cz.jzitnik.tui.utils.SpriteCombiner; import cz.jzitnik.tui.SpriteList; import cz.jzitnik.tui.utils.Numbers; diff --git a/src/main/java/cz/jzitnik/game/ui/InventoryClickHandler.java b/src/main/java/cz/jzitnik/game/ui/InventoryClickHandler.java index 65144a8..2bca08b 100644 --- a/src/main/java/cz/jzitnik/game/ui/InventoryClickHandler.java +++ b/src/main/java/cz/jzitnik/game/ui/InventoryClickHandler.java @@ -1,7 +1,7 @@ package cz.jzitnik.game.ui; import cz.jzitnik.game.Game; -import cz.jzitnik.game.items.InventoryItem; +import cz.jzitnik.game.entities.items.InventoryItem; import cz.jzitnik.tui.ScreenRenderer; import org.jline.terminal.MouseEvent; import org.jline.terminal.Terminal; diff --git a/src/main/java/cz/jzitnik/game/ui/InventoryDTO.java b/src/main/java/cz/jzitnik/game/ui/InventoryDTO.java index ca0a335..4f82ce0 100644 --- a/src/main/java/cz/jzitnik/game/ui/InventoryDTO.java +++ b/src/main/java/cz/jzitnik/game/ui/InventoryDTO.java @@ -1,6 +1,6 @@ package cz.jzitnik.game.ui; -import cz.jzitnik.game.items.InventoryItem; +import cz.jzitnik.game.entities.items.InventoryItem; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/cz/jzitnik/game/ui/SmallCraftingTable.java b/src/main/java/cz/jzitnik/game/ui/SmallCraftingTable.java index d9d76a1..da321c6 100644 --- a/src/main/java/cz/jzitnik/game/ui/SmallCraftingTable.java +++ b/src/main/java/cz/jzitnik/game/ui/SmallCraftingTable.java @@ -2,7 +2,7 @@ package cz.jzitnik.game.ui; import cz.jzitnik.game.crafting.CraftingRecipe; import cz.jzitnik.game.crafting.CraftingRecipeList; -import cz.jzitnik.game.items.InventoryItem; +import cz.jzitnik.game.entities.items.InventoryItem; import cz.jzitnik.tui.utils.SpriteCombiner; import cz.jzitnik.tui.SpriteList; import cz.jzitnik.tui.utils.Numbers; diff --git a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java index 65942ee..733d2ef 100644 --- a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java +++ b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java @@ -1,14 +1,13 @@ package cz.jzitnik.tui; -import cz.jzitnik.game.Block; +import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.Game; import cz.jzitnik.game.sprites.Steve; -import cz.jzitnik.game.ui.Chest; -import cz.jzitnik.game.ui.Furnace; +import cz.jzitnik.game.blocks.Chest; +import cz.jzitnik.game.blocks.Furnace; import cz.jzitnik.game.ui.Healthbar; import cz.jzitnik.tui.utils.SpriteCombiner; import lombok.Getter; -import lombok.RequiredArgsConstructor; import lombok.Setter; import org.jline.terminal.Terminal; @@ -16,10 +15,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -@RequiredArgsConstructor public class ScreenRenderer { - private SpriteList spriteList; - private Terminal terminal; + private final SpriteList spriteList; + private final Terminal terminal; public ScreenRenderer(SpriteList spriteList, Terminal terminal) { this.spriteList = spriteList; @@ -41,7 +39,7 @@ public class ScreenRenderer { return null; } - public void render(Game game) { + public synchronized void render(Game game) { var world = game.getWorld(); StringBuilder main = new StringBuilder(); main.append("\033[H\033[2J");