feat: Started implementing oak sapling logic

This commit is contained in:
2025-03-08 11:06:11 +01:00
parent eb899ac3a9
commit bc7480459a
28 changed files with 327 additions and 508 deletions

View File

@ -22,6 +22,7 @@ import lombok.Getter;
import lombok.Setter;
import org.jline.terminal.Terminal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@ -203,27 +204,34 @@ public class Game {
mining = false;
var blocks = world[y][x];
for (Block block : blocks) {
if (!block.isMineable()) {
continue;
}
var blocksCopy = new ArrayList<>(blocks);
CustomPlaceHandler customPlaceHandler = gameStates.dependencies.placeHandler
.get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId());
boolean giveItem = customPlaceHandler.mine(this, x, y);
if (block.getToolVariants().isEmpty()) {
// Add to inv
block.getDrops().forEach(inventory::addItem);
continue;
}
if (giveItem) {
for (Block block : blocksCopy) {
if (!block.isMineable()) {
continue;
}
var toolVariants = block.getToolVariants();
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())) {
block.getDrops().forEach(inventory::addItem);
if (block.getToolVariants().isEmpty()) {
// Add to inv
block.getDrops().forEach(inventory::addItem);
continue;
}
var toolVariants = block.getToolVariants();
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())) {
block.getDrops().forEach(inventory::addItem);
}
}
}
for (Block block : blocks) {
for (Block block : blocksCopy) {
if (block.getBlockId().equals("chest")) {
((Chest) block.getData()).breakBlock(this);
} else if (block.getBlockId().equals("furnace")) {
@ -231,9 +239,6 @@ public class Game {
}
}
CustomPlaceHandler customPlaceHandler = gameStates.dependencies.placeHandler
.get(blocks.stream().filter(Block::isMineable).toList().getFirst().getBlockId());
customPlaceHandler.mine(this, x, y);
inventory.getItemInHand().ifPresent(Item::use);
screenRenderer.render(this);

View File

@ -22,6 +22,9 @@ public class SpriteLoader {
// Work
FURNACE, CHEST, CRAFTING_TABLE, BED,
// Saplings
OAK_SAPLING,
// ENTITIES
STEVE, PIG, SHEEP, COW,
@ -34,7 +37,7 @@ public class SpriteLoader {
ITEM_STICK, ITEM_LEATHER,
// Block Items
ITEM_DIRT, ITEM_OAK_LOG, ITEM_OAK_PLANKS, ITEM_COBBLESTONE, ITEM_STONE, ITEM_OAK_DOOR, ITEM_WOOL,
ITEM_DIRT, ITEM_OAK_LOG, ITEM_OAK_LEAF, ITEM_OAK_PLANKS, ITEM_COBBLESTONE, ITEM_STONE, ITEM_OAK_DOOR, ITEM_WOOL, ITEM_OBSIDIAN,
// Ore Items
ITEM_COAL_ORE, ITEM_IRON_ORE, ITEM_GOLD_ORE, ITEM_DIAMOND_ORE,
@ -59,7 +62,7 @@ public class SpriteLoader {
BUCKET, WATER_BUCKET, LAVA_BUCKET, MILK_BUCKET,
// Food
ITEM_PORKCHOP, ITEM_COOKED_PORKCHOP, ITEM_MUTTON, ITEM_COOKED_MUTTON, ITEM_BEEF, ITEM_STEAK,
ITEM_PORKCHOP, ITEM_COOKED_PORKCHOP, ITEM_MUTTON, ITEM_COOKED_MUTTON, ITEM_BEEF, ITEM_STEAK, ITEM_APPLE,
}
public static final HashMap<SPRITES, Sprite> SPRITES_MAP = new HashMap<>();
@ -100,6 +103,9 @@ public class SpriteLoader {
SPRITES_MAP.put(SPRITES.CHEST, new SimpleSprite("chest.ans"));
SPRITES_MAP.put(SPRITES.BED, new Bed());
// Saplings
SPRITES_MAP.put(SPRITES.OAK_SAPLING, new SimpleSprite("oak_sapling.ans"));
// ENTITIES
SPRITES_MAP.put(SPRITES.STEVE, new Steve());
SPRITES_MAP.put(SPRITES.PIG, new Pig());
@ -120,11 +126,13 @@ public class SpriteLoader {
// Block Items
SPRITES_MAP.put(SPRITES.ITEM_DIRT, new SimpleSprite("items/dirt.ans"));
SPRITES_MAP.put(SPRITES.ITEM_OAK_LOG, new SimpleSprite("items/oak_log.ans"));
SPRITES_MAP.put(SPRITES.ITEM_OAK_LEAF, new SimpleSprite("items/oak_leaves.ans"));
SPRITES_MAP.put(SPRITES.ITEM_OAK_PLANKS, new SimpleSprite("items/oak_planks.ans"));
SPRITES_MAP.put(SPRITES.ITEM_COBBLESTONE, new SimpleSprite("items/cobblestone.ans"));
SPRITES_MAP.put(SPRITES.ITEM_STONE, new SimpleSprite("items/stone.ans"));
SPRITES_MAP.put(SPRITES.ITEM_OAK_DOOR, new SimpleSprite("oak_door/items/oak_door.ans"));
SPRITES_MAP.put(SPRITES.ITEM_WOOL, new WoolItem());
SPRITES_MAP.put(SPRITES.ITEM_OBSIDIAN, new SimpleSprite("items/obsidian.ans"));
// Ore Items
SPRITES_MAP.put(SPRITES.ITEM_COAL_ORE, new SimpleSprite("items/coal_ore.ans"));
@ -191,6 +199,7 @@ public class SpriteLoader {
SPRITES_MAP.put(SPRITES.ITEM_COOKED_MUTTON, new SimpleSprite("items/cooked_mutton.ans"));
SPRITES_MAP.put(SPRITES.ITEM_BEEF, new SimpleSprite("items/beef.ans"));
SPRITES_MAP.put(SPRITES.ITEM_STEAK, new SimpleSprite("items/steak.ans"));
SPRITES_MAP.put(SPRITES.ITEM_APPLE, new SimpleSprite("items/apple.ans"));
}
public static SpriteList<SPRITES> load() {

View File

@ -4,7 +4,6 @@ import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.logic.services.flowing.FlowingData;
import cz.jzitnik.game.sprites.Lava;
import cz.jzitnik.game.sprites.Water;
@BlockRegistry(value = "lava", drops = "lava_bucket")

View File

@ -0,0 +1,16 @@
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.entities.items.ItemType;
import java.util.ArrayList;
@BlockRegistry("oak_leaves")
public class OakLeavesBlock extends Block {
public OakLeavesBlock() {
super("oak_leaves", SpriteLoader.SPRITES.OAK_LEAF, 1, ItemType.SHEARS,
new ArrayList<>());
}
}

View File

@ -0,0 +1,14 @@
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.saplings.SaplingData;
@BlockRegistry("oak_sapling")
public class OakSaplingBlock extends Block {
public OakSaplingBlock() {
super("oak_sapling", SpriteLoader.SPRITES.OAK_SAPLING, 0);
setData(new SaplingData());
}
}

View File

@ -0,0 +1,13 @@
package cz.jzitnik.game.entities.items.registry.items;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.ItemRegistry;
import cz.jzitnik.game.entities.items.Item;
import cz.jzitnik.game.entities.items.ItemType;
@ItemRegistry("apple")
public class AppleItem extends Item {
public AppleItem() {
super("apple", "Apple", ItemType.FOOD, SpriteLoader.SPRITES.ITEM_APPLE, 2);
}
}

View File

@ -0,0 +1,13 @@
package cz.jzitnik.game.entities.items.registry.items;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.ItemRegistry;
import cz.jzitnik.game.entities.items.Item;
import cz.jzitnik.game.entities.items.ItemType;
@ItemRegistry("oak_leaves")
public class OakLeavesItem extends Item {
public OakLeavesItem() {
super("oak_leaves", "Oak leaves", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_OAK_LEAF);
}
}

View File

@ -0,0 +1,13 @@
package cz.jzitnik.game.entities.items.registry.items;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.ItemRegistry;
import cz.jzitnik.game.entities.items.Item;
import cz.jzitnik.game.entities.items.ItemType;
@ItemRegistry("oak_sapling")
public class OakSaplingItem extends Item {
public OakSaplingItem() {
super("oak_sapling", "Oak sapling", ItemType.BLOCK, SpriteLoader.SPRITES.OAK_SAPLING);
}
}

View File

@ -0,0 +1,13 @@
package cz.jzitnik.game.entities.items.registry.items;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.ItemRegistry;
import cz.jzitnik.game.entities.items.Item;
import cz.jzitnik.game.entities.items.ItemType;
@ItemRegistry("obsidian")
public class ObsidianItem extends Item {
public ObsidianItem() {
super("obsidian", "Obsidian", ItemType.BLOCK, SpriteLoader.SPRITES.ITEM_OBSIDIAN);
}
}

View File

@ -4,10 +4,8 @@ import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.entities.items.ItemBlockSupplier;
import cz.jzitnik.game.entities.items.ItemType;
import cz.jzitnik.game.sprites.Steve;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
@ -36,9 +34,6 @@ public class Generation {
// Spawn player at a valid starting point
world[terrainHeight[256] - 1][256].add(steveBlock2);
world[terrainHeight[256] - 2][256].add(steveBlock);
game.getInventory().addItem(ItemBlockSupplier.getItem("water_bucket"));
game.getInventory().addItem(ItemBlockSupplier.getItem("lava_bucket"));
}
private static void initializeWorld(List<Block>[][] world) {
@ -101,6 +96,7 @@ public class Generation {
if (treeBase - 3 < 0)
continue;
// Place trunk
for (int j = 0; j < 3; j++) {
if (treeBase - j >= 0) {
world[treeBase - j - 1][i].add(ItemBlockSupplier.getBlock("oak_log"));
@ -109,19 +105,22 @@ public class Generation {
int leafY = treeBase - 4;
// 2D Pyramid-shaped leaf placement
for (int layer = 0; layer < 3; layer++) {
int size = 5 - (layer * 2);
int offsetY = leafY - layer;
for (int dx = -size / 2; dx <= size / 2; dx++) {
for (int dy = -size / 2; dy <= size / 2; dy++) {
if (i + dx >= 0 && i + dx < world[0].length && offsetY >= 0) {
world[offsetY][i + dx].add(new Block("oak_leaves", SpriteLoader.SPRITES.OAK_LEAF, 1,
ItemType.SHEARS, new ArrayList<>()));
}
int x = i + dx;
int y = offsetY;
if (x >= 0 && x < world[0].length && y >= 0) {
world[y][x].add(ItemBlockSupplier.getBlock("oak_leaves"));
}
}
}
}
}
}

View File

@ -5,5 +5,5 @@ import cz.jzitnik.game.Game;
public interface CustomPlaceHandler {
boolean place(Game game, int x, int y);
void mine(Game game, int x, int y);
boolean mine(Game game, int x, int y);
}

View File

@ -18,9 +18,11 @@ public class DefaultPlaceHandler implements CustomPlaceHandler {
}
@Override
public void mine(Game game, int x, int y) {
public boolean mine(Game game, int x, int y) {
var blocks = game.getWorld()[y][x];
blocks.removeAll(blocks.stream().filter(i -> !i.getBlockId().equals("air")).toList());
return true;
}
}

View File

@ -47,7 +47,7 @@ public class BedPlaceHandler implements CustomPlaceHandler {
}
@Override
public void mine(Game game, int x, int y) {
public boolean mine(Game game, int x, int y) {
var blocks = game.getWorld()[y][x];
Block block = blocks.stream().filter(b -> b.getBlockId().equals("bed")).toList().getFirst();
@ -62,5 +62,7 @@ public class BedPlaceHandler implements CustomPlaceHandler {
}
blocks.removeAll(blocks.stream().filter(i -> !i.getBlockId().equals("air")).toList());
return true;
}
}

View File

@ -1,33 +0,0 @@
package cz.jzitnik.game.handlers.place.handlers;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.PlaceHandler;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.entities.items.Item;
import cz.jzitnik.game.handlers.place.CustomPlaceHandler;
@PlaceHandler("bucket")
public class BucketPlaceHandler implements CustomPlaceHandler {
@Override
public boolean place(Game game, int x, int y) {
return false;
}
@Override
public void mine(Game game, int x, int y) {
var world = game.getWorld();
var blocks = world[y][x];
var flowingBlocks = blocks.stream().filter(Block::isFlowing).toList();
if (flowingBlocks.isEmpty()) {
return;
}
var flowingBlock = flowingBlocks.getFirst();
var items = flowingBlock.getDrops();
for (Item item : items) {
game.getInventory().addItem(item);
}
blocks.remove(flowingBlock);
}
}

View File

@ -36,7 +36,7 @@ public class DoorPlaceHandler implements CustomPlaceHandler {
}
@Override
public void mine(Game game, int x, int y) {
public boolean mine(Game game, int x, int y) {
var blocks = game.getWorld()[y][x];
Block block = blocks.stream().filter(b -> b.getBlockId().equals("oak_door")).toList().getFirst();
@ -53,5 +53,7 @@ public class DoorPlaceHandler implements CustomPlaceHandler {
}
blocks.removeAll(blocks.stream().filter(i -> !i.getBlockId().equals("air")).toList());
return true;
}
}

View File

@ -0,0 +1,55 @@
package cz.jzitnik.game.handlers.place.handlers;
import cz.jzitnik.game.annotations.PlaceHandler;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.entities.items.InventoryItem;
import cz.jzitnik.game.entities.items.ItemBlockSupplier;
import cz.jzitnik.game.entities.items.ItemType;
import cz.jzitnik.game.handlers.place.CustomPlaceHandler;
import java.util.Random;
@PlaceHandler("oak_leaves")
public class OakLeavesMineHandler implements CustomPlaceHandler {
@Override
public boolean place(Game game, int x, int y) {
var blocks = game.getWorld()[y][x];
var inventory = game.getInventory();
blocks.add(inventory.getItemInHand().get().getBlock().get());
blocks.removeAll(blocks.stream().filter(Block::isFlowing).toList());
inventory.decreaseItemInHand();
return true;
}
@Override
public boolean mine(Game game, int x, int y) {
var blocks = game.getWorld()[y][x];
blocks.removeAll(blocks.stream().filter(i -> !i.getBlockId().equals("air")).toList());
Random random = new Random();
int percentage = random.nextInt(100);
// If mined with sheers drop leaf
if (game.getInventory().getItemInHand().isPresent() && game.getInventory().getItemInHand().get().getType() == ItemType.SHEARS) {
return true;
}
if (percentage < 2) {
// Apple
game.getInventory().addItem(ItemBlockSupplier.getItem("apple"));
} else if (percentage < 2 + 4) {
// Sticks
game.getInventory().addItem(new InventoryItem(random.nextInt(2) + 1, ItemBlockSupplier.getItem("stick")));
} else if (percentage < 2 + 4 + 5) {
// Sapling
game.getInventory().addItem(ItemBlockSupplier.getItem("oak_sapling"));
}
return false;
}
}

View File

@ -97,18 +97,11 @@ public class FlowingLogic implements CustomLogicInterface {
var targetBlocks = world[newY][newX];
boolean hasWater = targetBlocks.stream().anyMatch(b -> b.getBlockId().equals("water"));
boolean hasLava = targetBlocks.stream().anyMatch(b -> b.getBlockId().equals("lava"));
boolean isLavaSource = hasLava && targetBlocks.stream().anyMatch(b -> ((FlowingData) b.getData()).isSource());
if (liquidId.equals("water") && hasLava) {
if (isLavaSource) {
world[newY][newX].add(ItemBlockSupplier.getBlock("obsidian"));
} else {
world[newY][newX].add(ItemBlockSupplier.getBlock("cobblestone"));
}
return;
}
if (liquidId.equals("lava") && hasWater) {
world[newY][newX].add(ItemBlockSupplier.getBlock("cobblestone"));
return;
}

View File

@ -29,7 +29,6 @@ public class LavaWaterLogic implements CustomLogicInterface {
var water = blocks.stream().filter(block -> block.getBlockId().equals("water")).findFirst().get();
var lava = blocks.stream().filter(block -> block.getBlockId().equals("lava")).findFirst().get();
var waterData = (FlowingData) water.getData();
var lavaData = (FlowingData) lava.getData();
world[y][x].remove(water);

View File

@ -0,0 +1,17 @@
package cz.jzitnik.game.logic.services.saplings;
import lombok.Getter;
import lombok.Setter;
import java.util.Random;
@Getter
@Setter
public class SaplingData {
private int growWait;
public SaplingData() {
Random random = new Random();
growWait = random.nextInt(270) + 30;
}
}

View File

@ -0,0 +1,15 @@
package cz.jzitnik.game.logic.services.saplings;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.CustomLogic;
import cz.jzitnik.game.logic.CustomLogicInterface;
@CustomLogic
public class SaplingLogic implements CustomLogicInterface {
private static final int RADIUS = 30;
@Override
public void nextIteration(Game game) {
}
}

View File

@ -140,7 +140,7 @@ public class CowLogic
if (countCows(rstartX, rendX, rstartY, rendY, game) < 3 && random.nextInt(100) < 2) {
var spawnLocations = cowCanSpawn(rstartX, rendX, playerY, game);
if (!spawnLocations.isEmpty()) {
for (int i = 0; i < Math.min(random.nextInt(3) + 2, spawnLocations.size()); i++) {
for (int i = 0; i < Math.min(4, spawnLocations.size()); i++) {
var randomLocation = getRandomEntry(spawnLocations);
int x = randomLocation.getKey();
int y = randomLocation.getValue();

View File

@ -105,7 +105,7 @@ public class PigLogic
@Override
public void spawn(int playerX, int playerY, Game game, Terminal terminal) {
// Cordinates where player can see
// Coordinates where player can see
int[] data = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(),
terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length);
var world = game.getWorld();
@ -140,7 +140,7 @@ public class PigLogic
if (countPrasata(rstartX, rendX, rstartY, rendY, game) < 3 && random.nextInt(100) < 2) {
var spawnLocations = pigCanSpawn(rstartX, rendX, playerY, game);
if (!spawnLocations.isEmpty()) {
for (int i = 0; i < Math.min(random.nextInt(3) + 2, spawnLocations.size()); i++) {
for (int i = 0; i < Math.min(4, spawnLocations.size()); i++) {
var randomLocation = getRandomEntry(spawnLocations);
int x = randomLocation.getKey();
int y = randomLocation.getValue();

View File

@ -153,7 +153,7 @@ public class SheepLogic
if (countSheep(rstartX, rendX, rstartY, rendY, game) < 3 && random.nextInt(100) < 2) {
var spawnLocations = sheepCanSpawn(rstartX, rendX, playerY, game);
if (!spawnLocations.isEmpty()) {
for (int i = 0; i < Math.min(random.nextInt(3) + 2, spawnLocations.size()); i++) {
for (int i = 0; i < Math.min(4, spawnLocations.size()); i++) {
var randomLocation = getRandomEntry(spawnLocations);
int x = randomLocation.getKey();
int y = randomLocation.getValue();