From 9e42e7ade889f4d0b893a6aa6bfb41103a645ad9 Mon Sep 17 00:00:00 2001 From: jzitnik-dev Date: Sun, 9 Mar 2025 12:03:04 +0100 Subject: [PATCH] feat: Implemented events and added placeonsolid --- src/main/java/cz/jzitnik/game/Game.java | 89 +++++++++-------- .../game/annotations/MineEventHandler.java | 11 +++ .../game/annotations/PlaceEventHandler.java | 11 +++ .../annotations/PlaceOnSolidNoHandler.java | 11 +++ .../jzitnik/game/entities/Dependencies.java | 2 + .../items/registry/blocks/GrassBushBlock.java | 2 + .../items/registry/blocks/OakDoorBlock.java | 2 + .../handlers/events/EventHandlerProvider.java | 98 +++++++++++++++++++ .../mine/PlaceOnSolidMineHandler.java | 16 +++ .../game/handlers/place/PlaceHandler.java | 20 ++-- .../place/handlers/DoorPlaceHandler.java | 5 + 11 files changed, 220 insertions(+), 47 deletions(-) create mode 100644 src/main/java/cz/jzitnik/game/annotations/MineEventHandler.java create mode 100644 src/main/java/cz/jzitnik/game/annotations/PlaceEventHandler.java create mode 100644 src/main/java/cz/jzitnik/game/annotations/PlaceOnSolidNoHandler.java create mode 100644 src/main/java/cz/jzitnik/game/handlers/events/EventHandlerProvider.java create mode 100644 src/main/java/cz/jzitnik/game/handlers/events/handlers/mine/PlaceOnSolidMineHandler.java diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index 280e109..6daa649 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -210,50 +210,56 @@ public class Game implements Serializable { mining = false; - var blocks = world[y][x]; - var blocksCopy = new ArrayList<>(blocks); - CustomPlaceHandler customPlaceHandler = gameStates.dependencies.placeHandler - .get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId()); - boolean giveItem = customPlaceHandler.mine(this, x, y); - - if (giveItem) { - for (Block block : blocksCopy) { - if (!block.isMineable()) { - continue; - } - - if (block.getToolVariants().isEmpty()) { - // Add to inv - block.getDrops().forEach(inventory::addItem); - continue; - } - - var toolVariants = block.getToolVariants(); - if (inventory.getItemInHand().isPresent() - && inventory.getItemInHand().get().getToolVariant().isPresent() && block.getTool().isPresent() - && block.getTool().get().equals(inventory.getItemInHand().get().getType()) - && toolVariants.contains(inventory.getItemInHand().get().getToolVariant().get())) { - block.getDrops().forEach(inventory::addItem); - } - } - } - - for (Block block : blocksCopy) { - if (block.getBlockId().equals("chest")) { - ((Chest) block.getData()).breakBlock(this); - } else if (block.getBlockId().equals("furnace")) { - ((Furnace) block.getData()).breakBlock(this); - } - } - - inventory.getItemInHand().ifPresent(Item::use); - - screenRenderer.render(this); - - update(screenRenderer); + mineInstant(screenRenderer, x, y, true); }).start(); } + public void mineInstant(ScreenRenderer screenRenderer, int x, int y, boolean minedDirectly) { + var blocks = world[y][x]; + var blocksCopy = new ArrayList<>(blocks); + CustomPlaceHandler customPlaceHandler = gameStates.dependencies.placeHandler + .get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId()); + boolean giveItem = customPlaceHandler.mine(this, x, y); + + if (giveItem) { + for (Block block : blocksCopy) { + if (!block.isMineable()) { + continue; + } + + if (block.getToolVariants().isEmpty()) { + // Add to inv + block.getDrops().forEach(inventory::addItem); + continue; + } + + var toolVariants = block.getToolVariants(); + if (inventory.getItemInHand().isPresent() + && inventory.getItemInHand().get().getToolVariant().isPresent() && block.getTool().isPresent() + && block.getTool().get().equals(inventory.getItemInHand().get().getType()) + && toolVariants.contains(inventory.getItemInHand().get().getToolVariant().get()) && minedDirectly) { + block.getDrops().forEach(inventory::addItem); + } + } + } + + for (Block block : blocksCopy) { + if (block.getBlockId().equals("chest")) { + ((Chest) block.getData()).breakBlock(this); + } else if (block.getBlockId().equals("furnace")) { + ((Furnace) block.getData()).breakBlock(this); + } + } + + inventory.getItemInHand().ifPresent(Item::use); + + gameStates.dependencies.eventHandlerProvider.handleMine(screenRenderer, this, x, y); + + screenRenderer.render(this); + + update(screenRenderer); + } + public boolean isMineable(int x, int y, Terminal terminal) { List blocks = world[y][x]; @@ -379,6 +385,7 @@ public class Game implements Serializable { CustomPlaceHandler placeHandler = gameStates.dependencies.placeHandler.get(item.getId()); if (placeHandler.place(this, x, y)) { + gameStates.dependencies.eventHandlerProvider.handlePlace(screenRenderer, this, x, y); screenRenderer.render(this); } } diff --git a/src/main/java/cz/jzitnik/game/annotations/MineEventHandler.java b/src/main/java/cz/jzitnik/game/annotations/MineEventHandler.java new file mode 100644 index 0000000..81e91be --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/MineEventHandler.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; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface MineEventHandler { +} diff --git a/src/main/java/cz/jzitnik/game/annotations/PlaceEventHandler.java b/src/main/java/cz/jzitnik/game/annotations/PlaceEventHandler.java new file mode 100644 index 0000000..614e188 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/PlaceEventHandler.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; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface PlaceEventHandler { +} diff --git a/src/main/java/cz/jzitnik/game/annotations/PlaceOnSolidNoHandler.java b/src/main/java/cz/jzitnik/game/annotations/PlaceOnSolidNoHandler.java new file mode 100644 index 0000000..cc064c5 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/PlaceOnSolidNoHandler.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 PlaceOnSolidNoHandler { +} diff --git a/src/main/java/cz/jzitnik/game/entities/Dependencies.java b/src/main/java/cz/jzitnik/game/entities/Dependencies.java index 8839f1a..9a7061e 100644 --- a/src/main/java/cz/jzitnik/game/entities/Dependencies.java +++ b/src/main/java/cz/jzitnik/game/entities/Dependencies.java @@ -1,6 +1,7 @@ package cz.jzitnik.game.entities; import cz.jzitnik.game.GameSaver; +import cz.jzitnik.game.handlers.events.EventHandlerProvider; import cz.jzitnik.game.handlers.pickup.PickupHandlerProvider; import cz.jzitnik.game.handlers.place.PlaceHandler; import cz.jzitnik.game.mobs.EntityHurtAnimation; @@ -12,4 +13,5 @@ public class Dependencies { public EntityKill entityKill = new EntityKill(); public PickupHandlerProvider pickupHandlerProvider = new PickupHandlerProvider(); public GameSaver gameSaver = new GameSaver(); + public EventHandlerProvider eventHandlerProvider = new EventHandlerProvider(); } diff --git a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/GrassBushBlock.java b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/GrassBushBlock.java index 8e5d3a4..6f33586 100644 --- a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/GrassBushBlock.java +++ b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/GrassBushBlock.java @@ -3,8 +3,10 @@ package cz.jzitnik.game.entities.items.registry.blocks; import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.annotations.BlockDropPercentage; import cz.jzitnik.game.annotations.BlockRegistry; +import cz.jzitnik.game.annotations.PlaceOnSolid; import cz.jzitnik.game.entities.Block; +@PlaceOnSolid @BlockDropPercentage(13) @BlockRegistry(value = "grass_bush", drops = "wheat_seeds") public class GrassBushBlock extends Block { diff --git a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/OakDoorBlock.java b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/OakDoorBlock.java index 256d55e..c8e18b4 100644 --- a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/OakDoorBlock.java +++ b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/OakDoorBlock.java @@ -2,12 +2,14 @@ package cz.jzitnik.game.entities.items.registry.blocks; import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.annotations.BlockRegistry; +import cz.jzitnik.game.annotations.PlaceOnSolidNoHandler; import cz.jzitnik.game.blocks.OakDoorData; import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.items.ItemType; import java.util.ArrayList; +@PlaceOnSolidNoHandler @BlockRegistry("oak_door") public class OakDoorBlock extends Block { public OakDoorBlock() { diff --git a/src/main/java/cz/jzitnik/game/handlers/events/EventHandlerProvider.java b/src/main/java/cz/jzitnik/game/handlers/events/EventHandlerProvider.java new file mode 100644 index 0000000..a6e4cd8 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/handlers/events/EventHandlerProvider.java @@ -0,0 +1,98 @@ +package cz.jzitnik.game.handlers.events; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.MineEventHandler; +import cz.jzitnik.game.annotations.PlaceEventHandler; +import cz.jzitnik.tui.ScreenRenderer; +import org.reflections.Reflections; +import org.reflections.scanners.Scanners; +import org.reflections.util.ConfigurationBuilder; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class EventHandlerProvider { + @FunctionalInterface + public interface Function { + void accept(T t, U u, V v, W w); + } + + public final List> mineHandlers = new ArrayList<>(); + public final List> placeHandlers = new ArrayList<>(); + + public EventHandlerProvider() { + registerHandlers(); + } + + public void handlePlace(ScreenRenderer screenRenderer, Game game, int x, int y) { + for (Function handler : placeHandlers) { + handler.accept(screenRenderer, game, x, y); + } + } + + public void handleMine(ScreenRenderer screenRenderer, Game game, int x, int y) { + for (Function handler : mineHandlers) { + handler.accept(screenRenderer, game, x, y); + } + } + + private void registerHandlers() { + Reflections reflections = new Reflections( + new ConfigurationBuilder() + .forPackage("cz.jzitnik.game.handlers.events.handlers.mine") + .addScanners(Scanners.MethodsAnnotated) + ); + Set mineHandlers = reflections.getMethodsAnnotatedWith(MineEventHandler.class); + + for (Method method : mineHandlers) { + System.out.println(method); + if (method.getParameterCount() == 4 && + method.getParameterTypes()[0] == ScreenRenderer.class && + method.getParameterTypes()[1] == Game.class && + method.getParameterTypes()[2] == int.class && + method.getParameterTypes()[3] == int.class && + method.getReturnType() == void.class) { + try { + Object instance = method.getDeclaringClass().getDeclaredConstructor().newInstance(); + Function handler = (screenRenderer, game, x, y) -> { + try { + method.invoke(instance, screenRenderer, game, x, y); + } catch (Exception e) { + e.printStackTrace(); + } + }; + this.mineHandlers.add(handler); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + Set placeHandlers = reflections.getMethodsAnnotatedWith(PlaceEventHandler.class); + for (Method method : placeHandlers) { + if (method.getParameterCount() == 4 && + method.getParameterTypes()[0] == ScreenRenderer.class && + method.getParameterTypes()[1] == Game.class && + method.getParameterTypes()[2] == int.class && + method.getParameterTypes()[3] == int.class && + method.getReturnType() == void.class) { + + try { + Object instance = method.getDeclaringClass().getDeclaredConstructor().newInstance(); + Function handler = (screenRenderer, game, x, y) -> { + try { + method.invoke(instance, screenRenderer, game, x, y); + } catch (Exception e) { + e.printStackTrace(); + } + }; + this.placeHandlers.add(handler); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/src/main/java/cz/jzitnik/game/handlers/events/handlers/mine/PlaceOnSolidMineHandler.java b/src/main/java/cz/jzitnik/game/handlers/events/handlers/mine/PlaceOnSolidMineHandler.java new file mode 100644 index 0000000..a89c7e6 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/handlers/events/handlers/mine/PlaceOnSolidMineHandler.java @@ -0,0 +1,16 @@ +package cz.jzitnik.game.handlers.events.handlers.mine; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.MineEventHandler; +import cz.jzitnik.tui.ScreenRenderer; + +public class PlaceOnSolidMineHandler { + @MineEventHandler + public void placeOnSolidMineHandler(ScreenRenderer screenRenderer, Game game, int x, int y) { + var world = game.getWorld(); + + if (world[y-1][x].stream().anyMatch(block -> game.getGameStates().dependencies.placeHandler.isPlaceOnSolid(block.getBlockId()))) { + game.mineInstant(screenRenderer, x, y-1, false); + } + } +} 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 cd3c6f0..973fc72 100644 --- a/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java +++ b/src/main/java/cz/jzitnik/game/handlers/place/PlaceHandler.java @@ -1,17 +1,21 @@ package cz.jzitnik.game.handlers.place; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Set; -import cz.jzitnik.game.annotations.BlockDropPercentage; -import cz.jzitnik.game.annotations.BlockRegistry; -import cz.jzitnik.game.annotations.PlaceOnSolid; -import cz.jzitnik.game.annotations.ResetDataOnMine; +import cz.jzitnik.game.annotations.*; import org.reflections.Reflections; public class PlaceHandler { private final HashMap placeHandlerList = new HashMap<>(); private final CustomPlaceHandler defaultPlaceHandler = new DefaultPlaceHandler(); + private final List placeOnSolid = new ArrayList<>(); + + public boolean isPlaceOnSolid(String id) { + return placeOnSolid.contains(id); + } public boolean contains(String itemId) { return placeHandlerList.containsKey(itemId); @@ -53,15 +57,19 @@ public class PlaceHandler { .getTypesAnnotatedWith(BlockRegistry.class); for (Class clazz : blocks) { + var annotation = clazz.getAnnotation(BlockRegistry.class); + var id = annotation.value(); if (clazz.isAnnotationPresent(PlaceOnSolid.class) || clazz.isAnnotationPresent(ResetDataOnMine.class) || clazz.isAnnotationPresent(BlockDropPercentage.class)) { try { - var annotation = clazz.getAnnotation(BlockRegistry.class); - var id = annotation.value(); placeHandlerList.put(id, new CustomAnnotationHandler(clazz)); } catch (Exception e) { e.printStackTrace(); } } + + if (clazz.isAnnotationPresent(PlaceOnSolid.class) || clazz.isAnnotationPresent(PlaceOnSolidNoHandler.class)) { + placeOnSolid.add(id); + } } } } 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 a3faff6..71c3a11 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 @@ -13,11 +13,16 @@ public class DoorPlaceHandler implements CustomPlaceHandler { public boolean place(Game game, int x, int y) { var blocks = game.getWorld()[y][x]; var blocksTop = game.getWorld()[y - 1][x]; + var blocksBottom = game.getWorld()[y + 1][x]; if (!blocksTop.stream().allMatch(block -> block.getBlockId().equals("air"))) { return false; } + if (blocksBottom.stream().allMatch(Block::isGhost)) { + return false; + } + var inventory = game.getInventory(); Block block = inventory.getItemInHand().get().getBlock().get();