From 8b8c23dad4327f3686954e96ac27e3565e9323a9 Mon Sep 17 00:00:00 2001 From: jzitnik-dev Date: Sat, 22 Mar 2025 13:01:02 +0100 Subject: [PATCH] feat: Implemented block burning spreading --- src/main/java/cz/jzitnik/Main.java | 3 +- src/main/java/cz/jzitnik/game/Game.java | 11 +++ .../cz/jzitnik/game/annotations/Farmable.java | 1 + .../cz/jzitnik/game/annotations/Flamable.java | 12 +++ .../java/cz/jzitnik/game/entities/Block.java | 3 + .../registry/blocks/blocks/HaybaleBlock.java | 2 + .../registry/blocks/blocks/OakDoorBlock.java | 2 + .../registry/blocks/blocks/OakLogBlock.java | 2 + .../blocks/blocks/OakPlanksBlock.java | 2 + .../logic/services/farmable/FarmableData.java | 4 +- .../firespreading/FireSpreadingLogic.java | 73 +++++++++++++++++++ .../java/cz/jzitnik/tui/ScreenRenderer.java | 12 ++- 12 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 src/main/java/cz/jzitnik/game/annotations/Flamable.java create mode 100644 src/main/java/cz/jzitnik/game/logic/services/firespreading/FireSpreadingLogic.java diff --git a/src/main/java/cz/jzitnik/Main.java b/src/main/java/cz/jzitnik/Main.java index 4e434f9..fcaedd1 100644 --- a/src/main/java/cz/jzitnik/Main.java +++ b/src/main/java/cz/jzitnik/Main.java @@ -17,13 +17,14 @@ public class Main { try { Terminal terminal = TerminalBuilder.terminal(); terminal.enterRawMode(); - terminal.trackMouse(Terminal.MouseTracking.Any); if (!terminal.hasMouseSupport()) { System.out.println("Error: This terminal does not support mouse."); System.exit(1); } + terminal.trackMouse(Terminal.MouseTracking.Any); + var spriteList = SpriteLoader.load(); var screenRenderer = new ScreenRenderer(spriteList, terminal); var game = GameSaver.load(); diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index 1d5e477..92f184c 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -219,6 +219,11 @@ public class Game extends AutoTransientSupport { .get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId()); boolean giveItem = customPlaceHandler.mine(this, x, y); + blocksCopy.forEach((e) -> { + e.setOnFire(false); + e.setBurningTime(0); + }); + if (giveItem) { for (Block block : blocksCopy) { if (!block.isMineable()) { @@ -382,6 +387,12 @@ public class Game extends AutoTransientSupport { if (inventory.getItemInHand().isPresent()) { var item = inventory.getItemInHand().get(); toolUsed = gameStates.dependencies.toolUseProvider.handle(item.getType(), this, x, y); + if (toolUsed) { + boolean broken = item.use(); + if (broken) { + inventory.decreaseItemInHand(); + } + } } if (!toolUsed) { diff --git a/src/main/java/cz/jzitnik/game/annotations/Farmable.java b/src/main/java/cz/jzitnik/game/annotations/Farmable.java index 82ccf79..00d5186 100644 --- a/src/main/java/cz/jzitnik/game/annotations/Farmable.java +++ b/src/main/java/cz/jzitnik/game/annotations/Farmable.java @@ -7,6 +7,7 @@ import java.lang.annotation.ElementType; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) +@RequireAnnotation(BlockRegistry.class) public @interface Farmable { String value(); } diff --git a/src/main/java/cz/jzitnik/game/annotations/Flamable.java b/src/main/java/cz/jzitnik/game/annotations/Flamable.java new file mode 100644 index 0000000..1197b54 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/annotations/Flamable.java @@ -0,0 +1,12 @@ +package cz.jzitnik.game.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@RequireAnnotation(BlockRegistry.class) +public @interface Flamable { +} diff --git a/src/main/java/cz/jzitnik/game/entities/Block.java b/src/main/java/cz/jzitnik/game/entities/Block.java index 5cb15ea..0092732 100644 --- a/src/main/java/cz/jzitnik/game/entities/Block.java +++ b/src/main/java/cz/jzitnik/game/entities/Block.java @@ -28,6 +28,9 @@ public class Block implements Serializable { private boolean flowing = false; private boolean isMob = false; private int hp = 0; + private boolean onFire = false; + private int burningTime = 0; + private int burningTime2 = 0; public Block(String blockId, SpriteLoader.SPRITES sprite) { this.blockId = blockId; diff --git a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/HaybaleBlock.java b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/HaybaleBlock.java index ee5a058..aa61c1a 100644 --- a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/HaybaleBlock.java +++ b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/HaybaleBlock.java @@ -2,6 +2,7 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks; import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.annotations.BlockRegistry; +import cz.jzitnik.game.annotations.Flamable; import cz.jzitnik.game.annotations.ReduceFallDamage; import cz.jzitnik.game.core.reducefalldamage.HaybaleFallDamageReducer; import cz.jzitnik.game.entities.Block; @@ -9,6 +10,7 @@ import cz.jzitnik.game.entities.items.ItemType; import java.util.ArrayList; +@Flamable @BlockRegistry("haybale") @ReduceFallDamage(HaybaleFallDamageReducer.class) public class HaybaleBlock extends Block { diff --git a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakDoorBlock.java b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakDoorBlock.java index 4694920..3cb2438 100644 --- a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakDoorBlock.java +++ b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakDoorBlock.java @@ -2,6 +2,7 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks; import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.annotations.BlockRegistry; +import cz.jzitnik.game.annotations.Flamable; import cz.jzitnik.game.annotations.PlaceOnSolidNoHandler; import cz.jzitnik.game.blocks.OakDoorData; import cz.jzitnik.game.entities.Block; @@ -9,6 +10,7 @@ import cz.jzitnik.game.entities.items.ItemType; import java.util.ArrayList; +@Flamable @PlaceOnSolidNoHandler @BlockRegistry("oak_door") public class OakDoorBlock extends Block { diff --git a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakLogBlock.java b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakLogBlock.java index 3ec39df..9b3de3c 100644 --- a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakLogBlock.java +++ b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakLogBlock.java @@ -2,11 +2,13 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks; import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.annotations.BlockRegistry; +import cz.jzitnik.game.annotations.Flamable; import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.items.ItemType; import java.util.ArrayList; +@Flamable @BlockRegistry("oak_log") public class OakLogBlock extends Block { public OakLogBlock() { diff --git a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakPlanksBlock.java b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakPlanksBlock.java index 721b1d1..aa9cec6 100644 --- a/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakPlanksBlock.java +++ b/src/main/java/cz/jzitnik/game/entities/items/registry/blocks/blocks/OakPlanksBlock.java @@ -2,11 +2,13 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks; import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.annotations.BlockRegistry; +import cz.jzitnik.game.annotations.Flamable; import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.items.ItemType; import java.util.ArrayList; +@Flamable @BlockRegistry("oak_planks") public class OakPlanksBlock extends Block { public OakPlanksBlock() { diff --git a/src/main/java/cz/jzitnik/game/logic/services/farmable/FarmableData.java b/src/main/java/cz/jzitnik/game/logic/services/farmable/FarmableData.java index 6e58155..644ab74 100644 --- a/src/main/java/cz/jzitnik/game/logic/services/farmable/FarmableData.java +++ b/src/main/java/cz/jzitnik/game/logic/services/farmable/FarmableData.java @@ -1,11 +1,13 @@ package cz.jzitnik.game.logic.services.farmable; +import java.io.Serializable; + import lombok.Getter; import lombok.Setter; @Getter @Setter -public class FarmableData { +public class FarmableData implements Serializable { private int age = 0; private int state = 0; } diff --git a/src/main/java/cz/jzitnik/game/logic/services/firespreading/FireSpreadingLogic.java b/src/main/java/cz/jzitnik/game/logic/services/firespreading/FireSpreadingLogic.java new file mode 100644 index 0000000..42f8c65 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/logic/services/firespreading/FireSpreadingLogic.java @@ -0,0 +1,73 @@ +package cz.jzitnik.game.logic.services.firespreading; + +import java.util.List; +import java.util.Random; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.CustomLogic; +import cz.jzitnik.game.annotations.Flamable; +import cz.jzitnik.game.entities.Block; +import cz.jzitnik.game.logic.CustomLogicInterface; + +@CustomLogic +public class FireSpreadingLogic implements CustomLogicInterface { + private static final int RADIUS = 30; + private Random random = new Random(); + + @Override + public void nextIteration(Game game) { + var world = game.getWorld(); + int[] data = game.getPlayerCords(); + int playerX = data[0]; + int playerY = data[1]; + + int startX = Math.max(0, playerX - RADIUS); + int startY = Math.max(0, playerY - RADIUS); + int endX = Math.min(world[0].length - 1, playerX + RADIUS); + int endY = Math.min(world.length - 1, playerY + RADIUS); + + for (int y = startY; y <= endY; y++) { + for (int x = startX; x <= endX; x++) { + var blocks = world[y][x]; + + for (Block block : blocks) { + if (block.isOnFire()) { + int maxTime = random.nextInt(30) + 15; + block.setBurningTime2(block.getBurningTime2() + 1); + if (block.getBurningTime2() >= maxTime) { + world[y][x].remove(block); + } else { + spreadFireToNeighbors(world, x, y); + } + } + } + } + } + } + + private void spreadFireToNeighbors(List[][] world, int x, int y) { + checkAndSpreadFire(world, x + 1, y); + checkAndSpreadFire(world, x - 1, y); + checkAndSpreadFire(world, x, y + 1); + checkAndSpreadFire(world, x, y - 1); + } + + private void checkAndSpreadFire(List[][] world, int x, int y) { + if (x < 0 || y < 0 || x >= world[0].length || y >= world.length) { + return; + } + + var blocks = world[y][x]; + + for (Block block : blocks) { + if (block.getClass().isAnnotationPresent(Flamable.class) && !block.isOnFire()) { + int maxTime = random.nextInt(8) + 5; + block.setBurningTime(block.getBurningTime() + 1); + if (block.getBurningTime() >= maxTime) { + block.setOnFire(true); + block.setBurningTime(0); + } + } + } + } +} diff --git a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java index 609699b..f3d9c2d 100644 --- a/src/main/java/cz/jzitnik/tui/ScreenRenderer.java +++ b/src/main/java/cz/jzitnik/tui/ScreenRenderer.java @@ -112,12 +112,20 @@ public class ScreenRenderer { sprites.add(stringBuilder.toString()); } - if (blocks.stream().anyMatch(block -> block.getBlockId().equals("steve") && block.getSpriteState().get() == Steve.SteveState.SECOND) && game.getPlayer().isBurningState()) { + if (blocks.stream() + .anyMatch(block -> block.getBlockId().equals("steve") + && block.getSpriteState().get() == Steve.SteveState.SECOND) + && game.getPlayer().isBurningState()) { SimpleSprite fire = new SimpleSprite("fire.ans"); - sprites.add(fire.getSprite()); } + var burningBlocks = blocks.stream().filter(Block::isOnFire).toList(); + if (!burningBlocks.isEmpty()) { + SimpleSprite fire = new SimpleSprite("fire.ans"); + sprites.add(blocks.indexOf(burningBlocks.getLast()) + 1, fire.getSprite()); + } + String sprite = SpriteCombiner.combineSprites(sprites.toArray(String[]::new)); String[] spriteLines = sprite.split("\n");