feat: Implemented water
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package cz.jzitnik;
|
||||
|
||||
import cz.jzitnik.game.Game;
|
||||
import cz.jzitnik.game.logic.CustomLogicProvider;
|
||||
import cz.jzitnik.game.mobs.EntityLogicProvider;
|
||||
import cz.jzitnik.game.threads.HealthRegenerationThread;
|
||||
import cz.jzitnik.game.threads.HungerDrainThread;
|
||||
@ -35,6 +36,7 @@ public class Main {
|
||||
Thread healingThread = new HealthRegenerationThread(game.getPlayer());
|
||||
Thread hungerDrainThread = new HungerDrainThread(game.getPlayer());
|
||||
EntityLogicProvider entityLogicProvider = new EntityLogicProvider();
|
||||
CustomLogicProvider customLogicProvider = new CustomLogicProvider();
|
||||
|
||||
// Start all threads
|
||||
healingThread.start();
|
||||
@ -47,6 +49,11 @@ public class Main {
|
||||
} catch (Exception ignored) {
|
||||
// Yeah, yeah I know. Deal with it
|
||||
}
|
||||
try {
|
||||
customLogicProvider.update(game);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (game.getWindow() == Window.WORLD) {
|
||||
screenRenderer.render(game);
|
||||
|
@ -24,11 +24,12 @@ import org.jline.terminal.Terminal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@Getter
|
||||
public class Game {
|
||||
@SuppressWarnings("unchecked")
|
||||
private final List<Block>[][] world = (List<Block>[][]) new ArrayList[256][512];
|
||||
private final List<Block>[][] world = (List<Block>[][]) new CopyOnWriteArrayList[256][512];
|
||||
private final Player player = new Player();
|
||||
private boolean mining = false;
|
||||
@Setter
|
||||
@ -339,7 +340,7 @@ public class Game {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blocks.stream().allMatch(block -> block.getBlockId().equals("air"))) {
|
||||
if (!blocks.stream().allMatch(block -> block.getBlockId().equals("air") || block.isFlowing())) {
|
||||
RightClickHandlerProvider.handle(x, y, this, screenRenderer);
|
||||
screenRenderer.render(this);
|
||||
return;
|
||||
|
@ -13,6 +13,7 @@ public class SpriteLoader {
|
||||
|
||||
// Blocks
|
||||
AIR,
|
||||
WATER,
|
||||
DIRT,
|
||||
GRASS,
|
||||
STONE,
|
||||
@ -124,6 +125,7 @@ public class SpriteLoader {
|
||||
|
||||
// Block
|
||||
SPRITES_MAP.put(SPRITES.AIR, new Air());
|
||||
SPRITES_MAP.put(SPRITES.WATER, new Water());
|
||||
SPRITES_MAP.put(SPRITES.DIRT, new SimpleSprite("dirt.ans"));
|
||||
SPRITES_MAP.put(SPRITES.GRASS, new SimpleSprite("grass.ans"));
|
||||
SPRITES_MAP.put(SPRITES.STONE, new SimpleSprite("stone.ans"));
|
||||
|
11
src/main/java/cz/jzitnik/game/annotations/CustomLogic.java
Normal file
11
src/main/java/cz/jzitnik/game/annotations/CustomLogic.java
Normal 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.TYPE)
|
||||
public @interface CustomLogic {
|
||||
}
|
@ -5,7 +5,7 @@ import cz.jzitnik.game.annotations.CraftingRecipeRegistry;
|
||||
@CraftingRecipeRegistry(
|
||||
recipe = {
|
||||
"^.*_wool$", "^.*_wool$", "^.*_wool$",
|
||||
"oak_planks", "oak_planks", "oak_planks",
|
||||
"^oak_planks$", "^oak_planks$", "^oak_planks$",
|
||||
"_", "_", "_"
|
||||
},
|
||||
result = "bed",
|
||||
|
@ -25,6 +25,7 @@ public class Block {
|
||||
private List<ToolVariant> toolVariants = new ArrayList<>();
|
||||
private List<Item> drops = new ArrayList<>();
|
||||
private Object data = null;
|
||||
private boolean flowing = false;
|
||||
private boolean isMob = false;
|
||||
private int hp = 0;
|
||||
|
||||
|
@ -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.water.WaterData;
|
||||
import cz.jzitnik.game.sprites.Water;
|
||||
|
||||
@BlockRegistry("water")
|
||||
public class WaterBlock extends Block {
|
||||
public WaterBlock() {
|
||||
super("water", SpriteLoader.SPRITES.WATER);
|
||||
setMineable(false);
|
||||
setSpriteState(Water.WaterState.FIRST);
|
||||
setData(new WaterData());
|
||||
setFlowing(true);
|
||||
setGhost(true);
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import cz.jzitnik.game.sprites.Steve;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class Generation {
|
||||
public static void generateWorld(Game game) {
|
||||
@ -39,7 +40,7 @@ public class Generation {
|
||||
private static void initializeWorld(List<Block>[][] world) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
for (int j = 0; j < 512; j++) {
|
||||
world[i][j] = new ArrayList<>();
|
||||
world[i][j] = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cz.jzitnik.game.handlers.place;
|
||||
|
||||
import cz.jzitnik.game.Game;
|
||||
import cz.jzitnik.game.entities.Block;
|
||||
|
||||
public class DefaultPlaceHandler implements CustomPlaceHandler {
|
||||
@Override
|
||||
@ -9,6 +10,7 @@ public class DefaultPlaceHandler implements CustomPlaceHandler {
|
||||
var inventory = game.getInventory();
|
||||
|
||||
blocks.add(inventory.getItemInHand().get().getBlock().get());
|
||||
blocks.removeAll(blocks.stream().filter(Block::isFlowing).toList());
|
||||
|
||||
inventory.decreaseItemInHand();
|
||||
|
||||
|
@ -24,6 +24,7 @@ public class BedPlaceHandler implements CustomPlaceHandler {
|
||||
Block block2 = ItemBlockSupplier.getBlock("bed");
|
||||
block2.setSpriteState(Bed.BedState.RIGHT);
|
||||
blocksRight.add(block2);
|
||||
blocksRight.removeAll(blocksRight.stream().filter(Block::isFlowing).toList());
|
||||
|
||||
Block block = inventory.getItemInHand().get().getBlock().get();
|
||||
block.setSpriteState(Bed.BedState.LEFT);
|
||||
@ -32,11 +33,13 @@ public class BedPlaceHandler implements CustomPlaceHandler {
|
||||
Block block2 = ItemBlockSupplier.getBlock("bed");
|
||||
block2.setSpriteState(Bed.BedState.LEFT);
|
||||
blocksLeft.add(block2);
|
||||
blocksLeft.removeAll(blocksLeft.stream().filter(Block::isFlowing).toList());
|
||||
|
||||
Block block = inventory.getItemInHand().get().getBlock().get();
|
||||
block.setSpriteState(Bed.BedState.RIGHT);
|
||||
blocks.add(block);
|
||||
}
|
||||
blocks.removeAll(blocks.stream().filter(Block::isFlowing).toList());
|
||||
|
||||
inventory.decreaseItemInHand();
|
||||
|
||||
|
@ -23,10 +23,12 @@ public class DoorPlaceHandler implements CustomPlaceHandler {
|
||||
Block block = inventory.getItemInHand().get().getBlock().get();
|
||||
block.setSpriteState(OakDoor.OakDoorState.BOTTOMCLOSED);
|
||||
blocks.add(block);
|
||||
blocks.removeAll(blocks.stream().filter(Block::isFlowing).toList());
|
||||
|
||||
Block block2 = ItemBlockSupplier.getBlock("oak_door");
|
||||
block2.setSpriteState(OakDoor.OakDoorState.TOPCLOSED);
|
||||
blocksTop.add(block2);
|
||||
blocksTop.removeAll(blocksTop.stream().filter(Block::isFlowing).toList());
|
||||
|
||||
inventory.decreaseItemInHand();
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
package cz.jzitnik.game.logic;
|
||||
|
||||
import cz.jzitnik.game.Game;
|
||||
|
||||
public interface CustomLogicInterface {
|
||||
void nextIteration(Game game);
|
||||
}
|
39
src/main/java/cz/jzitnik/game/logic/CustomLogicProvider.java
Normal file
39
src/main/java/cz/jzitnik/game/logic/CustomLogicProvider.java
Normal file
@ -0,0 +1,39 @@
|
||||
package cz.jzitnik.game.logic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import cz.jzitnik.game.Game;
|
||||
import cz.jzitnik.game.annotations.CustomLogic;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
public class CustomLogicProvider {
|
||||
private final List<CustomLogicInterface> logicList = new ArrayList<>();
|
||||
|
||||
public void update(Game game) {
|
||||
for (CustomLogicInterface logicInterface : logicList) {
|
||||
logicInterface.nextIteration(game);
|
||||
}
|
||||
}
|
||||
|
||||
public CustomLogicProvider() {
|
||||
registerHandlers();
|
||||
}
|
||||
|
||||
private void registerHandlers() {
|
||||
Reflections reflections = new Reflections("cz.jzitnik.game.logic.services");
|
||||
Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(CustomLogic.class);
|
||||
|
||||
for (Class<?> clazz : handlerClasses) {
|
||||
if (CustomLogicInterface.class.isAssignableFrom(clazz)) {
|
||||
try {
|
||||
CustomLogicInterface instance = (CustomLogicInterface) clazz.getDeclaredConstructor().newInstance();
|
||||
logicList.add(instance);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package cz.jzitnik.game.logic.services.water;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class WaterData {
|
||||
private boolean isSource = true;
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package cz.jzitnik.game.logic.services.water;
|
||||
|
||||
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.Water;
|
||||
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 WaterLogic 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("water") && ((WaterData) 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("water") && !((WaterData) i.getData()).isSource()).toList());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (WaterBlock sourceBlock : sourceBlocks) {
|
||||
int x = sourceBlock.getX();
|
||||
int y = sourceBlock.getY();
|
||||
|
||||
flow(world, x, y, 5, new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
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("water");
|
||||
newWater.setSpriteState(Water.WaterState.get(5));
|
||||
((WaterData) 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("water");
|
||||
newWater.setSpriteState(Water.WaterState.get(strength - 1));
|
||||
((WaterData) 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("water");
|
||||
newWater.setSpriteState(Water.WaterState.get(strength - 1));
|
||||
((WaterData) newWater.getData()).setSource(false);
|
||||
world[y][x+1].add(newWater);
|
||||
flow(world, x + 1, y, strength - 1, visited);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean waterCanFlow(List<Block> blocks) {
|
||||
return blocks.stream().allMatch(block -> block.getBlockId().equals("steve") || block.getBlockId().equals("air") || block.isMob());
|
||||
}
|
||||
}
|
55
src/main/java/cz/jzitnik/game/sprites/Water.java
Normal file
55
src/main/java/cz/jzitnik/game/sprites/Water.java
Normal file
@ -0,0 +1,55 @@
|
||||
package cz.jzitnik.game.sprites;
|
||||
|
||||
import cz.jzitnik.tui.ResourceLoader;
|
||||
import cz.jzitnik.tui.Sprite;
|
||||
|
||||
public class Water extends Sprite {
|
||||
public enum WaterState {
|
||||
FIRST,
|
||||
SECOND,
|
||||
THIRD,
|
||||
FOURTH,
|
||||
FIFTH;
|
||||
|
||||
public static WaterState get(int x) {
|
||||
return switch (x) {
|
||||
case 5 -> FIRST;
|
||||
case 4 -> SECOND;
|
||||
case 3 -> THIRD;
|
||||
case 2 -> FOURTH;
|
||||
case 1 -> FIFTH;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + x);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public String getSprite() {
|
||||
return getSprite(WaterState.FIRST);
|
||||
}
|
||||
|
||||
public String getSprite(Enum e) {
|
||||
String[] resource = ResourceLoader.loadResource("water.ans").split("\n");
|
||||
|
||||
int numberFormTop = switch (e) {
|
||||
case WaterState.FIRST -> 0;
|
||||
case WaterState.SECOND -> 5;
|
||||
case WaterState.THIRD -> 10;
|
||||
case WaterState.FOURTH -> 15;
|
||||
case WaterState.FIFTH -> 20;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + e);
|
||||
};
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < 25; i++) {
|
||||
if (i < numberFormTop) {
|
||||
stringBuilder.append("\033[49m ".repeat(50));
|
||||
} else {
|
||||
stringBuilder.append(resource[i]);
|
||||
}
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user