feat: Implemented lava and infinite water source

This commit is contained in:
Jakub Žitník 2025-03-07 09:10:36 +01:00
parent 8aa0cba5df
commit 2065f8ea2a
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
12 changed files with 175 additions and 16 deletions

View File

@ -3,15 +3,10 @@ package cz.jzitnik.game.crafting.recipes;
import cz.jzitnik.game.annotations.CraftingRecipeRegistry; import cz.jzitnik.game.annotations.CraftingRecipeRegistry;
@CraftingRecipeRegistry( @CraftingRecipeRegistry(
/*recipe = { recipe = {
"iron_ingot", "_", "iron_ingot", "iron_ingot", "_", "iron_ingot",
"_", "iron_ingot", "_", "_", "iron_ingot", "_",
"_", "_", "_" "_", "_", "_"
},*/
recipe = {
"dirt", "_", "_",
"_", "_", "_",
"_", "_", "_"
}, },
result = "bucket", result = "bucket",
amount = 1 amount = 1

View File

@ -0,0 +1,19 @@
package cz.jzitnik.game.entities.items.registry.blocks;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.logic.services.flowing.lava.LavaData;
import cz.jzitnik.game.sprites.Lava;
@BlockRegistry(value = "lava", drops = "lava_bucket")
public class LavaBlock extends Block {
public LavaBlock() {
super("lava", SpriteLoader.SPRITES.LAVA);
setMineable(false);
setSpriteState(Lava.LavaState.FIRST);
setData(new LavaData());
setFlowing(true);
setGhost(true);
}
}

View File

@ -3,7 +3,7 @@ package cz.jzitnik.game.entities.items.registry.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.entities.Block; import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.logic.services.water.WaterData; import cz.jzitnik.game.logic.services.flowing.water.WaterData;
import cz.jzitnik.game.sprites.Water; import cz.jzitnik.game.sprites.Water;
@BlockRegistry(value = "water", drops = "water_bucket") @BlockRegistry(value = "water", drops = "water_bucket")

View File

@ -5,7 +5,7 @@ import cz.jzitnik.game.annotations.ItemRegistry;
import cz.jzitnik.game.entities.items.Item; import cz.jzitnik.game.entities.items.Item;
import cz.jzitnik.game.entities.items.ItemType; import cz.jzitnik.game.entities.items.ItemType;
@ItemRegistry("lava_bucket") @ItemRegistry(value = "lava_bucket", block = "lava")
public class LavaBucketItem extends Item { public class LavaBucketItem extends Item {
public LavaBucketItem() { public LavaBucketItem() {
super("lava_bucket", "Lava bucket", ItemType.PICKUPER, SpriteLoader.SPRITES.LAVA_BUCKET); super("lava_bucket", "Lava bucket", ItemType.PICKUPER, SpriteLoader.SPRITES.LAVA_BUCKET);

View File

@ -23,6 +23,7 @@ public class Generation {
Block steveBlock2 = new Block("steve", SpriteLoader.SPRITES.STEVE); Block steveBlock2 = new Block("steve", SpriteLoader.SPRITES.STEVE);
steveBlock2.setSpriteState(Steve.SteveState.SECOND); steveBlock2.setSpriteState(Steve.SteveState.SECOND);
steveBlock2.setGhost(true); steveBlock2.setGhost(true);
steveBlock2.setMob(true);
int[] terrainHeight = generateTerrain(); int[] terrainHeight = generateTerrain();
@ -35,7 +36,6 @@ public class Generation {
// Spawn player at a valid starting point // Spawn player at a valid starting point
world[terrainHeight[256] - 1][256].add(steveBlock2); world[terrainHeight[256] - 1][256].add(steveBlock2);
world[terrainHeight[256] - 2][256].add(steveBlock); world[terrainHeight[256] - 2][256].add(steveBlock);
world[terrainHeight[256] - 1][256 + 1].add(ItemBlockSupplier.getBlock("water"));
} }
private static void initializeWorld(List<Block>[][] world) { private static void initializeWorld(List<Block>[][] world) {

View File

@ -5,7 +5,7 @@ import cz.jzitnik.game.annotations.PickupHandler;
import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.entities.items.Item; import cz.jzitnik.game.entities.items.Item;
import cz.jzitnik.game.handlers.pickup.CustomPickupHandler; import cz.jzitnik.game.handlers.pickup.CustomPickupHandler;
import cz.jzitnik.game.logic.services.water.FlowingData; import cz.jzitnik.game.logic.services.flowing.FlowingData;
import java.util.List; import java.util.List;
@ -35,8 +35,23 @@ public class BucketPickupHandler implements CustomPickupHandler {
game.getInventory().decreaseItemInHand(); game.getInventory().decreaseItemInHand();
game.getInventory().addItem(returnItems); game.getInventory().addItem(returnItems);
if (!block.getBlockId().equals("water") || !isInfiniteWaterSource(game, x, y)) {
world[y][x].remove(world[y][x].stream().filter(Block::isFlowing).findFirst().get()); world[y][x].remove(world[y][x].stream().filter(Block::isFlowing).findFirst().get());
}
return true; return true;
} }
private boolean isInfiniteWaterSource(Game game, int x, int y) {
var world = game.getWorld();
var leftBlock = world[y][x - 1].stream().filter(Block::isFlowing).findFirst();
var rightBlock = world[y][x + 1].stream().filter(Block::isFlowing).findFirst();
if (leftBlock.isPresent() && ((FlowingData) leftBlock.get().getData()).isSource()) {
return true;
}
return rightBlock.isPresent() && ((FlowingData) rightBlock.get().getData()).isSource();
}
} }

View File

@ -1,4 +1,4 @@
package cz.jzitnik.game.logic.services.water; package cz.jzitnik.game.logic.services.flowing;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@ -0,0 +1,5 @@
package cz.jzitnik.game.logic.services.flowing.lava;
import cz.jzitnik.game.logic.services.flowing.FlowingData;
public class LavaData extends FlowingData {}

View File

@ -0,0 +1,123 @@
package cz.jzitnik.game.logic.services.flowing.lava;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.CustomLogic;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.entities.items.ItemBlockSupplier;
import cz.jzitnik.game.logic.CustomLogicInterface;
import cz.jzitnik.game.sprites.Lava;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@CustomLogic
public class LavaLogic implements CustomLogicInterface {
private static final int RADIUS = 20;
@AllArgsConstructor
@Getter
private static class WaterBlock {
private Block block;
private int x;
private int y;
}
@Override
public void nextIteration(Game game) {
int[] data = game.getPlayerCords();
var world = game.getWorld();
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);
List<WaterBlock> sourceBlocks = new ArrayList<>();
for (int y = startY; y <= endY; y++) {
for (int x = startX; x <= endX; x++) {
var blocks = world[y][x];
var waterSourceBlocks = blocks.stream().filter(block -> block.getBlockId().equals("lava") && ((LavaData) block.getData()).isSource()).toList();
if (!waterSourceBlocks.isEmpty()) {
sourceBlocks.add(new WaterBlock(waterSourceBlocks.getFirst(), x, y));
}
world[y][x].removeAll(blocks.stream().filter(i -> i.getBlockId().equals("lava") && !((LavaData) i.getData()).isSource()).toList());
}
}
for (WaterBlock sourceBlock : sourceBlocks) {
int x = sourceBlock.getX();
int y = sourceBlock.getY();
flow(world, x, y, 5, new HashSet<>());
}
checkFire(game, startX, endX, startY, endY);
}
public void flow(List<Block>[][] world, int x, int y, int strength, Set<Point> visited) {
if (y + 1 < world.length && waterCanFlow(world[y+1][x])) {
Block newWater = ItemBlockSupplier.getBlock("lava");
newWater.setSpriteState(Lava.LavaState.get(5));
((LavaData) newWater.getData()).setSource(false);
world[y+1][x].add(newWater);
flow(world, x, y + 1, 5, visited);
return;
}
if (strength == 1 || visited.contains(new Point(x, y))) {
return;
}
visited.add(new Point(x, y));
if (x - 1 >= 0 && waterCanFlow(world[y][x-1])) {
Block newWater = ItemBlockSupplier.getBlock("lava");
newWater.setSpriteState(Lava.LavaState.get(strength - 1));
((LavaData) newWater.getData()).setSource(false);
world[y][x-1].add(newWater);
flow(world, x - 1, y, strength - 1, visited);
}
if (x + 1 < world[y].length && waterCanFlow(world[y][x+1])) {
Block newWater = ItemBlockSupplier.getBlock("lava");
newWater.setSpriteState(Lava.LavaState.get(strength - 1));
((LavaData) newWater.getData()).setSource(false);
world[y][x+1].add(newWater);
flow(world, x + 1, y, strength - 1, visited);
}
}
public void checkFire(Game game, int startX, int endX, int startY, int endY) {
var world = game.getWorld();
for (int y = startY; y <= endY; y++) {
for (int x = startX; x <= endX; x++) {
var blocks = world[y][x];
if (blocks.stream().anyMatch(block -> block.getBlockId().equals("lava"))) {
var entities = blocks.stream().filter(Block::isMob).toList();
for (Block block : entities) {
if (block.getBlockId().equals("steve")) {
game.getPlayer().setHealth(game.getPlayer().getHealth() - 1);
continue;
}
block.decreaseHp(1);
}
}
}
}
}
private boolean waterCanFlow(List<Block> blocks) {
return blocks.stream().allMatch(block -> block.getBlockId().equals("steve") || block.getBlockId().equals("air") || block.isMob());
}
}

View File

@ -0,0 +1,5 @@
package cz.jzitnik.game.logic.services.flowing.water;
import cz.jzitnik.game.logic.services.flowing.FlowingData;
public class WaterData extends FlowingData {}

View File

@ -1,4 +1,4 @@
package cz.jzitnik.game.logic.services.water; package cz.jzitnik.game.logic.services.flowing.water;
import cz.jzitnik.game.Game; import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.CustomLogic; import cz.jzitnik.game.annotations.CustomLogic;

View File

@ -1,3 +0,0 @@
package cz.jzitnik.game.logic.services.water;
public class WaterData extends FlowingData {}