feat: Added killing mobs

This commit is contained in:
Jakub Žitník 2025-03-01 21:01:12 +01:00
parent 940c3f3fe5
commit 0e5ece68fb
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
30 changed files with 476 additions and 130 deletions

View File

@ -42,10 +42,10 @@ public class Main {
inputHandlerThread.start();
while (isRunning[0]) {
entityLogicProvider.update(game);
if (game.getWindow() == Window.WORLD) {
screenRenderer.render(game);
}
entityLogicProvider.update(game);
Thread.sleep(1000);
}

View File

@ -126,6 +126,40 @@ public class Game {
}).start();
}
public void hit(ScreenRenderer screenRenderer, int x, int y) {
if (mining || window != Window.WORLD) {
return;
}
List<Block> mobs = world[y][x].stream().filter(Block::isMob).toList();
for (Block mob : mobs) {
int dealDamage = inventory.getItemInHand().map(Item::getDealDamage).orElse(1);
if (mob.getHp() - dealDamage <= 0) {
// Mob is killed
gameStates.dependencies.entityKill.get(mob.getBlockId()).killed(this, mob);
world[y][x].remove(mob);
} else {
mob.decreaseHp(dealDamage);
mob.setSpriteState(gameStates.dependencies.entityHurtAnimation.get(mob.getBlockId()).setHurtAnimation(true, mob.getSpriteState().get()));
}
}
screenRenderer.render(this);
new Thread(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (Block mob : mobs) {
mob.setSpriteState(gameStates.dependencies.entityHurtAnimation.get(mob.getBlockId()).setHurtAnimation(false, mob.getSpriteState().get()));
}
screenRenderer.render(this);
}).start();
}
public void mine(ScreenRenderer screenRenderer, int x, int y) {
if (mining || window != Window.WORLD) {
return;
@ -219,14 +253,39 @@ public class Game {
int endY = data[3];
return
y >= startY && y < endY - 1 && x >= startX && x < endX - 1 &&
!blocks.stream().allMatch(block -> block.getBlockId().equals("air"))
y >= startY && y < endY - 1 && x >= startX && x < endX - 1
&& distanceX <= 5 && distanceY <= 5
&& !(playerX == x && playerY == y)
&& !(playerX == x && playerY - 1 == y)
&& blocks.stream().anyMatch(Block::isMineable);
}
public boolean isHitable(int x, int y, Terminal terminal) {
List<Block> blocks = world[y][x];
int[] cords = getPlayerCords();
int playerX = cords[0];
int playerY = cords[1];
int distanceX = Math.abs(playerX - x);
int distanceY = Math.abs(playerY - y);
int[] data = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), world[0].length, world.length);
int startX = data[0];
int endX = data[1];
int startY = data[2];
int endY = data[3];
return
y >= startY && y < endY - 1 && x >= startX && x < endX - 1
&& distanceX <= 5 && distanceY <= 5
&& !(playerX == x && playerY == y)
&& !(playerX == x && playerY - 1 == y)
&& blocks.stream().anyMatch(Block::isMob);
}
public void update(ScreenRenderer screenRenderer) {
while (true) {
try {
@ -256,6 +315,16 @@ public class Game {
if (window != Window.WORLD) {
return;
}
if (inventory.getItemInHand().isPresent() && inventory.getItemInHand().get().getType() == ItemType.FOOD) {
if (player.getHunger() >= 10) {
return;
}
player.setHunger(Math.min(10, player.getHunger() + inventory.getItemInHand().get().getAddHunger()));
inventory.decreaseItemInHand();
screenRenderer.render(this);
return;
}
var blocks = world[y][x];
int[] cords = getPlayerCords();

View File

@ -52,6 +52,9 @@ public class SpriteLoader {
HUNGER,
PIG,
ITEM_PORKCHOP,
ITEM_COOKED_PORKCHOP,
}
public static final HashMap<SPRITES, Sprite> SPRITES_MAP = new HashMap<>();
@ -90,6 +93,8 @@ public class SpriteLoader {
SPRITES_MAP.put(SPRITES.ITEM_CHEST, new SimpleSprite("items/chest.ans"));
SPRITES_MAP.put(SPRITES.ITEM_FURNACE, new SimpleSprite("items/furnace.ans"));
SPRITES_MAP.put(SPRITES.ITEM_OAK_DOOR, new SimpleSprite("oak_door/items/oak_door.ans"));
SPRITES_MAP.put(SPRITES.ITEM_PORKCHOP, new SimpleSprite("items/porkchop.ans"));
SPRITES_MAP.put(SPRITES.ITEM_COOKED_PORKCHOP, new SimpleSprite("items/cooked_porkchop.ans"));
SPRITES_MAP.put(SPRITES.HEART, new Heart());
SPRITES_MAP.put(SPRITES.HUNGER, new Hunger());

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 EntityHurtAnimationHandler {
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)
public @interface EntityKillHandler {
String value();
}

View File

@ -1,6 +1,5 @@
package cz.jzitnik.game.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

View File

@ -100,12 +100,6 @@ public class CraftingRecipeList {
{"oak_planks", "oak_planks", null},
{"oak_planks", "oak_planks", null}
}, () -> new InventoryItem(1, ItemBlockSupplier.Items.oakDoor())));
recipes.add(new CraftingRecipe(new String[][]{
{"dirt", null, null},
{null, null, null},
{null, null, null}
}, () -> new InventoryItem(1, ItemBlockSupplier.Items.oakDoor())));
}
public static Optional<CraftingRecipe> getRecipe(String[] r) {

View File

@ -26,6 +26,7 @@ public class Block {
private List<Item> drops = new ArrayList<>();
private Object data = null;
private boolean isMob = false;
private int hp = 0;
public Block(String blockId, SpriteLoader.SPRITES sprite) {
this.blockId = blockId;
@ -74,4 +75,8 @@ public class Block {
return decrease;
}
public void decreaseHp(int amount) {
hp -= amount;
}
}

View File

@ -1,7 +1,11 @@
package cz.jzitnik.game.entities;
import cz.jzitnik.game.handlers.place.PlaceHandler;
import cz.jzitnik.game.mobs.EntityHurtAnimation;
import cz.jzitnik.game.mobs.EntityKill;
public class Dependencies {
public PlaceHandler placeHandler = new PlaceHandler();
public EntityHurtAnimation entityHurtAnimation = new EntityHurtAnimation();
public EntityKill entityKill = new EntityKill();
}

View File

@ -19,6 +19,8 @@ public class Item {
private int durability;
private double miningDecrease = 0;
private int stackAmount = 64;
private int addHunger = 0;
private int dealDamage = 1;
private Optional<Block> block = Optional.empty();
public Item(String id, String name, ItemType type, SpriteLoader.SPRITES sprite, ToolVariant toolVariant, double miningDecrease, int durability, boolean stackable) {
@ -47,6 +49,14 @@ public class Item {
this.sprite = sprite;
}
public Item(String id, String name, ItemType type, SpriteLoader.SPRITES sprite, int addHunger) {
this.id = id;
this.name = name;
this.type = type;
this.sprite = sprite;
this.addHunger = addHunger;
}
public void use() {
durability--;
}

View File

@ -6,6 +6,7 @@ import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.blocks.Chest;
import cz.jzitnik.game.blocks.Furnace;
import cz.jzitnik.game.mobs.services.pig.PigData;
import cz.jzitnik.game.sprites.Pig;
import java.util.ArrayList;
import java.util.Arrays;
@ -148,6 +149,14 @@ public class ItemBlockSupplier {
public static Item oakDoor() {
return Helper.oakDoor(Blocks.oakDoor());
}
public static Item porkchop() {
return new Item("porkchop", "Porkchop", ItemType.FOOD, SpriteLoader.SPRITES.ITEM_PORKCHOP, 3);
}
public static Item cookedPorkchop() {
return new Item("cooked_porkchop", "Cooked porkchop", ItemType.FOOD, SpriteLoader.SPRITES.ITEM_COOKED_PORKCHOP, 4);
}
}
public static class Mobs {
@ -156,8 +165,10 @@ public class ItemBlockSupplier {
var block = new Block("pig", SpriteLoader.SPRITES.PIG);
block.setMob(true);
block.setGhost(true);
block.setSpriteState(Pig.PigState.RIGHT);
block.setMineable(false);
block.setData(new PigData());
block.setHp(3);
return block;
}
}

View File

@ -6,5 +6,6 @@ public enum ItemType {
AXE,
SHEARS,
BLOCK,
FOOD,
USELESS_ITEM
}

View File

@ -0,0 +1,36 @@
package cz.jzitnik.game.mobs;
import java.util.HashMap;
import java.util.Set;
import cz.jzitnik.game.annotations.EntityHurtAnimationHandler;
import org.reflections.Reflections;
public class EntityHurtAnimation {
private final HashMap<String, EntityHurtAnimationChanger> handlerList = new HashMap<>();
public EntityHurtAnimation() {
registerHandlers();
}
public EntityHurtAnimationChanger get(String key) {
return handlerList.get(key);
}
private void registerHandlers() {
Reflections reflections = new Reflections("cz.jzitnik.game.mobs.services");
Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(EntityHurtAnimationHandler.class);
for (Class<?> clazz : handlerClasses) {
if (EntityHurtAnimationChanger.class.isAssignableFrom(clazz)) {
try {
EntityHurtAnimationChanger handlerInstance = (EntityHurtAnimationChanger) clazz.getDeclaredConstructor().newInstance();
EntityHurtAnimationHandler annotation = clazz.getAnnotation(EntityHurtAnimationHandler.class);
handlerList.put(annotation.value(), handlerInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,5 @@
package cz.jzitnik.game.mobs;
public interface EntityHurtAnimationChanger {
Enum setHurtAnimation(boolean hurt, Enum current);
}

View File

@ -0,0 +1,36 @@
package cz.jzitnik.game.mobs;
import java.util.HashMap;
import java.util.Set;
import cz.jzitnik.game.annotations.EntityKillHandler;
import org.reflections.Reflections;
public class EntityKill {
private final HashMap<String, EntityKillInterface> handlerList = new HashMap<>();
public EntityKill() {
registerHandlers();
}
public EntityKillInterface get(String key) {
return handlerList.get(key);
}
private void registerHandlers() {
Reflections reflections = new Reflections("cz.jzitnik.game.mobs.services");
Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(EntityKillHandler.class);
for (Class<?> clazz : handlerClasses) {
if (EntityKillInterface.class.isAssignableFrom(clazz)) {
try {
EntityKillInterface handlerInstance = (EntityKillInterface) clazz.getDeclaredConstructor().newInstance();
EntityKillHandler annotation = clazz.getAnnotation(EntityKillHandler.class);
handlerList.put(annotation.value(), handlerInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,8 @@
package cz.jzitnik.game.mobs;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.entities.Block;
public interface EntityKillInterface {
void killed(Game game, Block mob);
}

View File

@ -1,22 +1,27 @@
package cz.jzitnik.game.mobs.services.pig;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.EntityHurtAnimationHandler;
import cz.jzitnik.game.annotations.EntityKillHandler;
import cz.jzitnik.game.annotations.EntityLogic;
import cz.jzitnik.game.annotations.EntitySpawn;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.entities.items.InventoryItem;
import cz.jzitnik.game.entities.items.ItemBlockSupplier;
import cz.jzitnik.game.mobs.EntityLogicInterface;
import cz.jzitnik.game.mobs.EntityLogicProvider;
import cz.jzitnik.game.mobs.EntitySpawnInterface;
import cz.jzitnik.game.mobs.*;
import cz.jzitnik.game.sprites.Pig;
import cz.jzitnik.tui.ScreenMovingCalculationProvider;
import org.jline.terminal.Terminal;
import java.util.*;
import static cz.jzitnik.game.sprites.Pig.PigState.*;
@EntitySpawn
@EntityLogic("pig")
public class PigLogic implements EntityLogicInterface, EntitySpawnInterface {
@EntityHurtAnimationHandler("pig")
@EntityKillHandler("pig")
public class PigLogic implements EntityLogicInterface, EntitySpawnInterface, EntityHurtAnimationChanger, EntityKillInterface {
private final Random random = new Random();
@Override
@ -32,24 +37,29 @@ public class PigLogic implements EntityLogicInterface, EntitySpawnInterface {
int newPigX = pigX;
int newPigY = pigY;
// Reduce movement cooldown
if (pigData.getMovementCooldown() > 0) {
pigData.setMovementCooldown(pigData.getMovementCooldown() - 1);
return; // Skip movement this iteration
return;
}
// Determine movement direction
int direction = pigData.getLastDirection();
if (random.nextInt(10) < 3) { // 30% chance to change direction
if (random.nextInt(10) < 1) { // 10% chance to change direction
direction = -direction;
}
pigData.setLastDirection(direction);
// Update sprite direction
if (direction == 1) {
pig.setSpriteState(Pig.PigState.RIGHT);
if (pig.getSpriteState().get() == RIGHT_HURT || pig.getSpriteState().get() == LEFT_HURT) {
pig.setSpriteState(RIGHT_HURT);
} else {
pig.setSpriteState(RIGHT);
}
} else {
pig.setSpriteState(Pig.PigState.LEFT);
if (pig.getSpriteState().get() == RIGHT_HURT || pig.getSpriteState().get() == LEFT_HURT) {
pig.setSpriteState(LEFT_HURT);
} else {
pig.setSpriteState(LEFT);
}
}
List<Block> blocksAhead = world[pigY][pigX + direction];
@ -58,14 +68,13 @@ public class PigLogic implements EntityLogicInterface, EntitySpawnInterface {
world[pigY][pigX + direction].add(pig);
newPigX = pigX + direction;
updated = true;
pigData.setJumpAttempts(0); // Reset jump attempts when moving forward
pigData.setJumpAttempts(0);
} else {
List<Block> blocksAboveAhead = world[pigY - 1][pigX + direction];
List<Block> blocksTwoAboveAhead = world[pigY - 2][pigX + direction];
// Jump if there is only one block height obstacle and limit jump attempts
if (!game.isSolid(blocksAboveAhead) && game.isSolid(blocksAhead) && !game.isSolid(blocksTwoAboveAhead)) {
if (pigData.getJumpAttempts() < 2) { // Limit jumping attempts to prevent infinite jumping
if (pigData.getJumpAttempts() < 2) {
world[pigY][pigX].remove(pig);
world[pigY - 1][pigX + direction].add(pig);
newPigX = pigX + direction;
@ -76,10 +85,9 @@ public class PigLogic implements EntityLogicInterface, EntitySpawnInterface {
}
}
// Falling logic (avoid long falls)
while (updated) {
if (!game.isSolid(world[newPigY + 1][newPigX])) {
if (newPigY - pigY < 3) { // Only fall if it's at most 2 blocks drop
if (newPigY - pigY < 3) {
world[newPigY][newPigX].remove(pig);
world[newPigY + 1][newPigX].add(pig);
newPigY++;
@ -91,7 +99,6 @@ public class PigLogic implements EntityLogicInterface, EntitySpawnInterface {
}
}
// Apply movement cooldown to slow down movement
pigData.setMovementCooldown(random.nextInt(3) + 1); // 1-3 iterations cooldown
}
@ -133,7 +140,7 @@ public class PigLogic implements EntityLogicInterface, EntitySpawnInterface {
var spawnLocations = pigCanSpawn(rstartX, rendX, playerY, game);
if (!spawnLocations.isEmpty()) {
System.out.println(spawnLocations.size());
for (int i = 0; i < Math.min(4, spawnLocations.size()); i++) {
for (int i = 0; i < Math.min(random.nextInt(3) + 2, spawnLocations.size()); i++) {
var randomLocation = getRandomEntry(spawnLocations);
int x = randomLocation.getKey();
int y = randomLocation.getValue();
@ -174,4 +181,27 @@ public class PigLogic implements EntityLogicInterface, EntitySpawnInterface {
return pigAmount;
}
public Pig.PigState setHurtAnimation(boolean hurt, Enum current) {
if (hurt) {
return switch (current) {
case LEFT_HURT,LEFT -> LEFT_HURT;
case RIGHT_HURT,RIGHT -> RIGHT_HURT;
default -> throw new IllegalStateException("Unexpected value: " + current);
};
}
return switch (current) {
case LEFT_HURT,LEFT -> LEFT;
case RIGHT_HURT,RIGHT -> RIGHT;
default -> throw new IllegalStateException("Unexpected value: " + current);
};
}
@Override
public void killed(Game game, Block mob) {
int amount = random.nextInt(3) + 1;
InventoryItem inventoryItem = new InventoryItem(amount, ItemBlockSupplier.Items.porkchop());
game.getInventory().addItem(inventoryItem);
}
}

View File

@ -11,6 +11,7 @@ public class Smelting {
public static final HashMap<String, Supplier<Item>> smeltingList = new HashMap<>();
static {
smeltingList.put("cobblestone", ItemBlockSupplier.Items::stone);
smeltingList.put("porkchop", ItemBlockSupplier.Items::cookedPorkchop);
}
public static final HashMap<String, Double> fuelList = new HashMap<>();

View File

@ -7,6 +7,8 @@ public class Pig extends Sprite {
public enum PigState{
LEFT,
RIGHT,
LEFT_HURT,
RIGHT_HURT
}
public String getSprite() {
@ -16,8 +18,10 @@ public class Pig extends Sprite {
public String getSprite(Enum e) {
return ResourceLoader.loadResource(
switch (e) {
case PigState.LEFT -> "mobs/pigrev.ans";
case PigState.RIGHT -> "mobs/pig.ans";
case PigState.LEFT -> "mobs/pig/left.ans";
case PigState.RIGHT -> "mobs/pig/right.ans";
case PigState.LEFT_HURT -> "mobs/pig/lefthurt.ans";
case PigState.RIGHT_HURT -> "mobs/pig/righthurt.ans";
default -> throw new IllegalStateException("Unexpected value: " + e);
}
);

View File

@ -58,6 +58,10 @@ public class MouseHandler {
int blockX = startX + (mouseX / 50); // 50 chars wide per sprite
int blockY = startY + (mouseY / 25); // 25 lines high per sprite
if (game.isHitable(blockX, blockY, terminal)) {
game.hit(screenRenderer, blockX, blockY);
}
if (game.isMineable(blockX, blockY, terminal)) {
screenRenderer.setSelectedBlock(Optional.empty());
game.mine(screenRenderer, blockX, blockY);

View File

@ -1,25 +1,25 @@
               
                  
               
                
                
                  
                  
                   
             
             
              
             
                  
               
               
              
                  
                
                
          
               
             
              
                   
                
                 
                
                     
            
                  
                    
                 
               
               
             
                  
             
             
                   
                
           
                 
                 
                
             
                  
                   
              
            
            

View File

@ -1,25 +1,25 @@
                    
                     
                      
                         
                       
                    
                         
                     
                
                     
                  
              
                       
              
              
                       
                     
                   
                  
                  
                     
                     
                  
                     
                   
                    
                   
                     
                     
                      
              
                       
               
                    
                   
                
                  
                       
                      
                    
                  
                
                    
                 
                
                 
                   
               
            
            

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                                                  
                                         
                                     
                                     
                                  
                              
                              
                             
                           
                           
                           
                           
                       
                           
                           
                                
                                 
                                 
                                       
                                       
                                                  
                                                  

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                                        
                                           
                                        
                                     
                                     
                                 
                             
                            
                          
                               
                           
                               
                             
                              
                                
                                
                                 
                                       
                                      
                                        
                                       
                                                  

View File

@ -1,25 +0,0 @@
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                         
                                         
                          
                          
                          
                         
                        
                         
                         
                               
                               
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                         
                                         
                         
                         
                          
                         
                         
                        
                          
                               
                               
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                         
                                         
                         
                        
                          
                         
                         
                         
                        
                                
                                
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                         
                                         
                         
                          
                          
                         
                         
                        
                        
                               
                               
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@ -0,0 +1,25 @@
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                                  
                                         
                                         
                        
                        
                          
                        
                         
                        
                        
                                
                                
                                         
                                         
                                         
                                         
                                         
                                         
                                         

View File

@ -1,25 +0,0 @@