feat: Implemented block burning spreading

This commit is contained in:
Jakub Žitník 2025-03-22 13:01:02 +01:00
parent 36e57bbb8d
commit 8b8c23dad4
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
12 changed files with 123 additions and 4 deletions

View File

@ -17,13 +17,14 @@ public class Main {
try { try {
Terminal terminal = TerminalBuilder.terminal(); Terminal terminal = TerminalBuilder.terminal();
terminal.enterRawMode(); terminal.enterRawMode();
terminal.trackMouse(Terminal.MouseTracking.Any);
if (!terminal.hasMouseSupport()) { if (!terminal.hasMouseSupport()) {
System.out.println("Error: This terminal does not support mouse."); System.out.println("Error: This terminal does not support mouse.");
System.exit(1); System.exit(1);
} }
terminal.trackMouse(Terminal.MouseTracking.Any);
var spriteList = SpriteLoader.load(); var spriteList = SpriteLoader.load();
var screenRenderer = new ScreenRenderer(spriteList, terminal); var screenRenderer = new ScreenRenderer(spriteList, terminal);
var game = GameSaver.load(); var game = GameSaver.load();

View File

@ -219,6 +219,11 @@ public class Game extends AutoTransientSupport {
.get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId()); .get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId());
boolean giveItem = customPlaceHandler.mine(this, x, y); boolean giveItem = customPlaceHandler.mine(this, x, y);
blocksCopy.forEach((e) -> {
e.setOnFire(false);
e.setBurningTime(0);
});
if (giveItem) { if (giveItem) {
for (Block block : blocksCopy) { for (Block block : blocksCopy) {
if (!block.isMineable()) { if (!block.isMineable()) {
@ -382,6 +387,12 @@ public class Game extends AutoTransientSupport {
if (inventory.getItemInHand().isPresent()) { if (inventory.getItemInHand().isPresent()) {
var item = inventory.getItemInHand().get(); var item = inventory.getItemInHand().get();
toolUsed = gameStates.dependencies.toolUseProvider.handle(item.getType(), this, x, y); toolUsed = gameStates.dependencies.toolUseProvider.handle(item.getType(), this, x, y);
if (toolUsed) {
boolean broken = item.use();
if (broken) {
inventory.decreaseItemInHand();
}
}
} }
if (!toolUsed) { if (!toolUsed) {

View File

@ -7,6 +7,7 @@ import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
@RequireAnnotation(BlockRegistry.class)
public @interface Farmable { public @interface Farmable {
String value(); String value();
} }

View File

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

View File

@ -28,6 +28,9 @@ public class Block implements Serializable {
private boolean flowing = false; private boolean flowing = false;
private boolean isMob = false; private boolean isMob = false;
private int hp = 0; private int hp = 0;
private boolean onFire = false;
private int burningTime = 0;
private int burningTime2 = 0;
public Block(String blockId, SpriteLoader.SPRITES sprite) { public Block(String blockId, SpriteLoader.SPRITES sprite) {
this.blockId = blockId; this.blockId = blockId;

View File

@ -2,6 +2,7 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks;
import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.BlockRegistry; import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.Flamable;
import cz.jzitnik.game.annotations.ReduceFallDamage; import cz.jzitnik.game.annotations.ReduceFallDamage;
import cz.jzitnik.game.core.reducefalldamage.HaybaleFallDamageReducer; import cz.jzitnik.game.core.reducefalldamage.HaybaleFallDamageReducer;
import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.Block;
@ -9,6 +10,7 @@ import cz.jzitnik.game.entities.items.ItemType;
import java.util.ArrayList; import java.util.ArrayList;
@Flamable
@BlockRegistry("haybale") @BlockRegistry("haybale")
@ReduceFallDamage(HaybaleFallDamageReducer.class) @ReduceFallDamage(HaybaleFallDamageReducer.class)
public class HaybaleBlock extends Block { public class HaybaleBlock extends Block {

View File

@ -2,6 +2,7 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks;
import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.BlockRegistry; import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.Flamable;
import cz.jzitnik.game.annotations.PlaceOnSolidNoHandler; import cz.jzitnik.game.annotations.PlaceOnSolidNoHandler;
import cz.jzitnik.game.blocks.OakDoorData; import cz.jzitnik.game.blocks.OakDoorData;
import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.Block;
@ -9,6 +10,7 @@ import cz.jzitnik.game.entities.items.ItemType;
import java.util.ArrayList; import java.util.ArrayList;
@Flamable
@PlaceOnSolidNoHandler @PlaceOnSolidNoHandler
@BlockRegistry("oak_door") @BlockRegistry("oak_door")
public class OakDoorBlock extends Block { public class OakDoorBlock extends Block {

View File

@ -2,11 +2,13 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks;
import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.BlockRegistry; import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.Flamable;
import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.entities.items.ItemType; import cz.jzitnik.game.entities.items.ItemType;
import java.util.ArrayList; import java.util.ArrayList;
@Flamable
@BlockRegistry("oak_log") @BlockRegistry("oak_log")
public class OakLogBlock extends Block { public class OakLogBlock extends Block {
public OakLogBlock() { public OakLogBlock() {

View File

@ -2,11 +2,13 @@ package cz.jzitnik.game.entities.items.registry.blocks.blocks;
import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.BlockRegistry; import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.Flamable;
import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.entities.items.ItemType; import cz.jzitnik.game.entities.items.ItemType;
import java.util.ArrayList; import java.util.ArrayList;
@Flamable
@BlockRegistry("oak_planks") @BlockRegistry("oak_planks")
public class OakPlanksBlock extends Block { public class OakPlanksBlock extends Block {
public OakPlanksBlock() { public OakPlanksBlock() {

View File

@ -1,11 +1,13 @@
package cz.jzitnik.game.logic.services.farmable; package cz.jzitnik.game.logic.services.farmable;
import java.io.Serializable;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@Getter @Getter
@Setter @Setter
public class FarmableData { public class FarmableData implements Serializable {
private int age = 0; private int age = 0;
private int state = 0; private int state = 0;
} }

View File

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

View File

@ -112,12 +112,20 @@ public class ScreenRenderer {
sprites.add(stringBuilder.toString()); 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"); SimpleSprite fire = new SimpleSprite("fire.ans");
sprites.add(fire.getSprite()); 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 sprite = SpriteCombiner.combineSprites(sprites.toArray(String[]::new));
String[] spriteLines = sprite.split("\n"); String[] spriteLines = sprite.split("\n");