feat: Implemented events and added placeonsolid

This commit is contained in:
Jakub Žitník 2025-03-09 12:03:04 +01:00
parent 26f35da0f3
commit 9e42e7ade8
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
11 changed files with 220 additions and 47 deletions

View File

@ -210,6 +210,11 @@ public class Game implements Serializable {
mining = false;
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
@ -232,7 +237,7 @@ public class Game implements Serializable {
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())) {
&& toolVariants.contains(inventory.getItemInHand().get().getToolVariant().get()) && minedDirectly) {
block.getDrops().forEach(inventory::addItem);
}
}
@ -248,10 +253,11 @@ public class Game implements Serializable {
inventory.getItemInHand().ifPresent(Item::use);
gameStates.dependencies.eventHandlerProvider.handleMine(screenRenderer, this, x, y);
screenRenderer.render(this);
update(screenRenderer);
}).start();
}
public boolean isMineable(int x, int y, Terminal terminal) {
@ -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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {

View File

@ -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() {

View File

@ -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<T, U, V, W> {
void accept(T t, U u, V v, W w);
}
public final List<Function<ScreenRenderer, Game, Integer, Integer>> mineHandlers = new ArrayList<>();
public final List<Function<ScreenRenderer, Game, Integer, Integer>> placeHandlers = new ArrayList<>();
public EventHandlerProvider() {
registerHandlers();
}
public void handlePlace(ScreenRenderer screenRenderer, Game game, int x, int y) {
for (Function<ScreenRenderer, Game, Integer, Integer> handler : placeHandlers) {
handler.accept(screenRenderer, game, x, y);
}
}
public void handleMine(ScreenRenderer screenRenderer, Game game, int x, int y) {
for (Function<ScreenRenderer, Game, Integer, Integer> 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<Method> 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<ScreenRenderer, Game, Integer, Integer> 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<Method> 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<ScreenRenderer, Game, Integer, Integer> 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();
}
}
}
}
}

View File

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

View File

@ -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<String, CustomPlaceHandler> placeHandlerList = new HashMap<>();
private final CustomPlaceHandler defaultPlaceHandler = new DefaultPlaceHandler();
private final List<String> 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) {
if (clazz.isAnnotationPresent(PlaceOnSolid.class) || clazz.isAnnotationPresent(ResetDataOnMine.class) || clazz.isAnnotationPresent(BlockDropPercentage.class)) {
try {
var annotation = clazz.getAnnotation(BlockRegistry.class);
var id = annotation.value();
if (clazz.isAnnotationPresent(PlaceOnSolid.class) || clazz.isAnnotationPresent(ResetDataOnMine.class) || clazz.isAnnotationPresent(BlockDropPercentage.class)) {
try {
placeHandlerList.put(id, new CustomAnnotationHandler(clazz));
} catch (Exception e) {
e.printStackTrace();
}
}
if (clazz.isAnnotationPresent(PlaceOnSolid.class) || clazz.isAnnotationPresent(PlaceOnSolidNoHandler.class)) {
placeOnSolid.add(id);
}
}
}
}

View File

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