feat: Implemented grass bush

This commit is contained in:
Jakub Žitník 2025-03-08 18:14:28 +01:00
parent 5e45851e04
commit 59f0a70ea9
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
10 changed files with 161 additions and 2 deletions

View File

@ -14,6 +14,9 @@ public class SpriteLoader {
// Blocks // Blocks
AIR, WATER, LAVA, DIRT, GRASS, STONE, BEDROCK, COBBLESTONE, WOOL, OAK_LOG, OAK_LEAF, OAK_PLANKS, OAK_DOOR, OBSIDIAN, AIR, WATER, LAVA, DIRT, GRASS, STONE, BEDROCK, COBBLESTONE, WOOL, OAK_LOG, OAK_LEAF, OAK_PLANKS, OAK_DOOR, OBSIDIAN,
// Flowers etc
GRASS_BUSH,
// Ores // Ores
COAL_ORE, IRON_ORE, GOLD_ORE, DIAMOND_ORE, COAL_ORE, IRON_ORE, GOLD_ORE, DIAMOND_ORE,
@ -63,6 +66,9 @@ public class SpriteLoader {
BUCKET, WATER_BUCKET, LAVA_BUCKET, MILK_BUCKET, BUCKET, WATER_BUCKET, LAVA_BUCKET, MILK_BUCKET,
// Food // Food
ITEM_PORKCHOP, ITEM_COOKED_PORKCHOP, ITEM_MUTTON, ITEM_COOKED_MUTTON, ITEM_BEEF, ITEM_STEAK, ITEM_APPLE, ITEM_PORKCHOP, ITEM_COOKED_PORKCHOP, ITEM_MUTTON, ITEM_COOKED_MUTTON, ITEM_BEEF, ITEM_STEAK, ITEM_APPLE,
// Seeds
ITEM_WHEAT_SEEDS,
} }
public static final HashMap<SPRITES, Sprite> SPRITES_MAP = new HashMap<>(); public static final HashMap<SPRITES, Sprite> SPRITES_MAP = new HashMap<>();
@ -86,6 +92,9 @@ public class SpriteLoader {
SPRITES_MAP.put(SPRITES.COBBLESTONE, new SimpleSprite("cobblestone.ans")); SPRITES_MAP.put(SPRITES.COBBLESTONE, new SimpleSprite("cobblestone.ans"));
SPRITES_MAP.put(SPRITES.OBSIDIAN, new SimpleSprite("obsidian.ans")); SPRITES_MAP.put(SPRITES.OBSIDIAN, new SimpleSprite("obsidian.ans"));
// Flowers etc.
SPRITES_MAP.put(SPRITES.GRASS_BUSH, new SimpleSprite("grass_bush.ans"));
// Ores // Ores
SPRITES_MAP.put(SPRITES.COAL_ORE, new SimpleSprite("coal_ore.ans")); SPRITES_MAP.put(SPRITES.COAL_ORE, new SimpleSprite("coal_ore.ans"));
SPRITES_MAP.put(SPRITES.IRON_ORE, new SimpleSprite("iron_ore.ans")); SPRITES_MAP.put(SPRITES.IRON_ORE, new SimpleSprite("iron_ore.ans"));
@ -200,6 +209,9 @@ public class SpriteLoader {
SPRITES_MAP.put(SPRITES.ITEM_BEEF, new SimpleSprite("items/beef.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_STEAK, new SimpleSprite("items/steak.ans"));
SPRITES_MAP.put(SPRITES.ITEM_APPLE, new SimpleSprite("items/apple.ans")); SPRITES_MAP.put(SPRITES.ITEM_APPLE, new SimpleSprite("items/apple.ans"));
// Seeds
SPRITES_MAP.put(SPRITES.ITEM_WHEAT_SEEDS, new SimpleSprite("items/wheat_seeds.ans"));
} }
public static SpriteList<SPRITES> load() { public static SpriteList<SPRITES> load() {

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)
public @interface BlockDropPercentage {
int value();
}

View File

@ -0,0 +1,15 @@
package cz.jzitnik.game.entities.items.registry.blocks;
import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.game.annotations.BlockDropPercentage;
import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.entities.Block;
@BlockDropPercentage(100)
@BlockRegistry(value = "grass_bush", drops = "wheat_seeds")
public class GrassBushBlock extends Block {
public GrassBushBlock() {
super("grass_bush", SpriteLoader.SPRITES.GRASS_BUSH, 0);
setGhost(true);
}
}

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(value = "wheat_seeds", block = "grass_bush")
public class WheatSeedsItem extends Item {
public WheatSeedsItem() {
super("wheat_seeds", "Wheat seeds", ItemType.USELESS_ITEM, SpriteLoader.SPRITES.ITEM_WHEAT_SEEDS);
}
}

View File

@ -63,11 +63,17 @@ public class Generation {
} }
private static void populateWorld(List<Block>[][] world, int[] terrainHeight) { private static void populateWorld(List<Block>[][] world, int[] terrainHeight) {
Random random = new Random();
for (int i = 0; i < 512; i++) { for (int i = 0; i < 512; i++) {
int hillHeight = terrainHeight[i]; int hillHeight = terrainHeight[i];
world[hillHeight][i].add(ItemBlockSupplier.getBlock("grass")); world[hillHeight][i].add(ItemBlockSupplier.getBlock("grass"));
if (random.nextDouble() < 0.1 && !isTreeNearby(world, i, hillHeight)) {
world[hillHeight - 1][i].add(ItemBlockSupplier.getBlock("grass_bush"));
}
for (int j = 1; j <= 4; j++) { for (int j = 1; j <= 4; j++) {
if (hillHeight + j < 256) { if (hillHeight + j < 256) {
world[hillHeight + j][i].add(ItemBlockSupplier.getBlock("dirt")); world[hillHeight + j][i].add(ItemBlockSupplier.getBlock("dirt"));
@ -81,6 +87,7 @@ public class Generation {
world[255][i].add(new Block("bedrock", SpriteLoader.SPRITES.BEDROCK)); world[255][i].add(new Block("bedrock", SpriteLoader.SPRITES.BEDROCK));
} }
// Fill air blocks
for (List<Block>[] lists : world) { for (List<Block>[] lists : world) {
for (List<Block> list : lists) { for (List<Block> list : lists) {
list.addFirst(new Block("air", SpriteLoader.SPRITES.AIR, true, false)); list.addFirst(new Block("air", SpriteLoader.SPRITES.AIR, true, false));
@ -88,6 +95,25 @@ public class Generation {
} }
} }
private static boolean isTreeNearby(List<Block>[][] world, int x, int y) {
int radius = 3; // Check within a 3-block radius for trees
for (int dx = -radius; dx <= radius; dx++) {
for (int dy = -radius; dy <= radius; dy++) {
int nx = x + dx;
int ny = y + dy;
if (nx >= 0 && nx < 512 && ny >= 0 && ny < 256) {
for (Block block : world[ny][nx]) {
if (block.getBlockId().equals("oak_log") || block.getBlockId().equals("oak_leaves")) {
return true;
}
}
}
}
}
return false;
}
private static void plantTrees(List<Block>[][] world, int[] terrainHeight) { private static void plantTrees(List<Block>[][] world, int[] terrainHeight) {
Random random = new Random(); Random random = new Random();
for (int i = 10; i < 502; i += random.nextInt(20) + 20) { for (int i = 10; i < 502; i += random.nextInt(20) + 20) {

View File

@ -1,12 +1,14 @@
package cz.jzitnik.game.handlers.place; package cz.jzitnik.game.handlers.place;
import cz.jzitnik.game.Game; import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.BlockDropPercentage;
import cz.jzitnik.game.annotations.PlaceOnSolid; import cz.jzitnik.game.annotations.PlaceOnSolid;
import cz.jzitnik.game.annotations.ResetDataOnMine; import cz.jzitnik.game.annotations.ResetDataOnMine;
import cz.jzitnik.game.entities.Block; import cz.jzitnik.game.entities.Block;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.Random;
public class CustomAnnotationHandler implements CustomPlaceHandler { public class CustomAnnotationHandler implements CustomPlaceHandler {
private final Class<?> clazz; private final Class<?> clazz;
@ -31,10 +33,24 @@ public class CustomAnnotationHandler implements CustomPlaceHandler {
resetDataOnMine(game, x, y); resetDataOnMine(game, x, y);
} }
return defaultPlaceHandler.mine(game, x, y); boolean drop = true;
if (clazz.isAnnotationPresent(BlockDropPercentage.class)) {
drop = blockDropPercentage(game, x, y);
}
defaultPlaceHandler.mine(game, x, y);
return drop;
} }
private boolean blockDropPercentage(Game game, int x, int y) {
var annotation = clazz.getAnnotation(BlockDropPercentage.class);
int percentage = annotation.value();
Random random = new Random();
return random.nextInt(100) < percentage;
}
private boolean placeOnSolid(Game game, int x, int y) { private boolean placeOnSolid(Game game, int x, int y) {
var blocksBottom = game.getWorld()[y + 1][x]; var blocksBottom = game.getWorld()[y + 1][x];

View File

@ -3,6 +3,7 @@ package cz.jzitnik.game.handlers.place;
import java.util.HashMap; import java.util.HashMap;
import java.util.Set; import java.util.Set;
import cz.jzitnik.game.annotations.BlockDropPercentage;
import cz.jzitnik.game.annotations.BlockRegistry; import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.PlaceOnSolid; import cz.jzitnik.game.annotations.PlaceOnSolid;
import cz.jzitnik.game.annotations.ResetDataOnMine; import cz.jzitnik.game.annotations.ResetDataOnMine;
@ -52,7 +53,7 @@ public class PlaceHandler {
.getTypesAnnotatedWith(BlockRegistry.class); .getTypesAnnotatedWith(BlockRegistry.class);
for (Class<?> clazz : blocks) { for (Class<?> clazz : blocks) {
if (clazz.isAnnotationPresent(PlaceOnSolid.class) || clazz.isAnnotationPresent(ResetDataOnMine.class)) { if (clazz.isAnnotationPresent(PlaceOnSolid.class) || clazz.isAnnotationPresent(ResetDataOnMine.class) || clazz.isAnnotationPresent(BlockDropPercentage.class)) {
try { try {
var annotation = clazz.getAnnotation(BlockRegistry.class); var annotation = clazz.getAnnotation(BlockRegistry.class);
var id = annotation.value(); var id = annotation.value();

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                                           
                                           
                                            
                                         
                                          
                                     
                                    
                                    
                                    
                                  
                                  
                                  
                                   
                                 
                               
                               
                       
                        
                      
                       
                         
                        

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                              
                                              
                                                
                                            
                                            
                                     
                                   
                                      
                                           
                                           
                                      
                                          
                                          
                                             
                                                  
                                                  
                                                  
                                                  
                                                  

View File

@ -1,5 +1,6 @@
package cz.jzitnik.game.handlers.place; package cz.jzitnik.game.handlers.place;
import cz.jzitnik.game.annotations.BlockDropPercentage;
import cz.jzitnik.game.annotations.BlockRegistry; import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.PlaceOnSolid; import cz.jzitnik.game.annotations.PlaceOnSolid;
import cz.jzitnik.game.annotations.ResetDataOnMine; import cz.jzitnik.game.annotations.ResetDataOnMine;
@ -68,6 +69,19 @@ class PlaceHandlerTest {
} }
} }
@Test
@DisplayName("All blocks annotated with @BlockDropPercentage must be also annotated with @BlockRegistry")
void blockDropPercentageTest() {
Reflections reflections = new Reflections("cz.jzitnik.game.entities.items.registry.blocks");
Set<Class<?>> handlerClasses = reflections
.getTypesAnnotatedWith(BlockDropPercentage.class);
for (Class<?> clazz : handlerClasses) {
boolean hasAnnotation = clazz.isAnnotationPresent(BlockRegistry.class);
assertTrue(hasAnnotation, "Class " + clazz.getName() + " is annotated with @BlockDropPercentage but does not have annotation @BlockRegistry");
}
}
@Test @Test
@DisplayName("All blocks annotated with @ResetDataOnMine must have data class with default constructor") @DisplayName("All blocks annotated with @ResetDataOnMine must have data class with default constructor")
void dataOnMineHasDataTest() { void dataOnMineHasDataTest() {