diff --git a/src/main/java/cz/jzitnik/game/Game.java b/src/main/java/cz/jzitnik/game/Game.java index 62c94f9..e138dc7 100644 --- a/src/main/java/cz/jzitnik/game/Game.java +++ b/src/main/java/cz/jzitnik/game/Game.java @@ -57,7 +57,9 @@ public class Game { private transient GameStates gameStates = new GameStates(this); private Stats stats = new Stats(); - /** Current time of day in the game (0–600 range). */ + /** + * Current time of day in the game (0–600 range). + */ @Setter private int daytime = 0; // @@ -83,9 +85,9 @@ public class Game { var steveData = (SteveData) block.getData(); if (steveData.isTop()) { - return new int[] { j, i + 1 }; + return new int[]{j, i + 1}; } else { - return new int[] { j, i }; + return new int[]{j, i}; } } } @@ -208,12 +210,16 @@ public class Game { 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); + gameStates.dependencies.entityKill.get(mob.getBlockId()).killed(this, mob, x, y); world[y][x].remove(mob); } else { mob.decreaseHp(dealDamage); mob.setSpriteState(gameStates.dependencies.entityHurtAnimation.get(mob.getBlockId()) .setHurtAnimation(true, mob.getSpriteState().get())); + if (mob.getLinkedMobTexture() != null) { + mob.getLinkedMobTexture().setSpriteState(gameStates.dependencies.entityHurtAnimation.get(mob.getBlockId()) + .setHurtAnimation(true, mob.getLinkedMobTexture().getSpriteState().get())); + } } } screenRenderer.render(this); @@ -228,6 +234,11 @@ public class Game { for (Block mob : mobs) { mob.setSpriteState(gameStates.dependencies.entityHurtAnimation.get(mob.getBlockId()) .setHurtAnimation(false, mob.getSpriteState().get())); + + if (mob.getLinkedMobTexture() != null) { + mob.getLinkedMobTexture().setSpriteState(gameStates.dependencies.entityHurtAnimation.get(mob.getBlockId()) + .setHurtAnimation(false, mob.getLinkedMobTexture().getSpriteState().get())); + } } screenRenderer.render(this); }).start(); @@ -387,9 +398,9 @@ public class Game { /** * Checks whether a block is valid for mining based on proximity and visibility. * - * @param x x-coordinate. - * @param y y-coordinate. - * @param terminal terminal context used to determine screen bounds. + * @param x x-coordinate. + * @param y y-coordinate. + * @param terminal terminal context used to determine screen bounds. * @return true if mineable, false otherwise. */ public boolean isMineable(int x, int y, Terminal terminal) { @@ -420,9 +431,9 @@ public class Game { /** * Checks whether a block is valid for hitting (attacking). * - * @param x x-coordinate. - * @param y y-coordinate. - * @param terminal terminal context used to determine screen bounds. + * @param x x-coordinate. + * @param y y-coordinate. + * @param terminal terminal context used to determine screen bounds. * @return true if a mob can be hit at the given location. */ public boolean isHitable(int x, int y, Terminal terminal) { diff --git a/src/main/java/cz/jzitnik/game/entities/Block.java b/src/main/java/cz/jzitnik/game/entities/Block.java index 8490b00..ec7f88d 100644 --- a/src/main/java/cz/jzitnik/game/entities/Block.java +++ b/src/main/java/cz/jzitnik/game/entities/Block.java @@ -32,6 +32,7 @@ public class Block { private boolean onFire = false; private int burningTime = 0; private int burningTime2 = 0; + private Block linkedMobTexture; public Block(String blockId, SpriteLoader.SPRITES sprite) { this.blockId = blockId; diff --git a/src/main/java/cz/jzitnik/game/mobs/EntityKillInterface.java b/src/main/java/cz/jzitnik/game/mobs/EntityKillInterface.java index ed993aa..c90d427 100644 --- a/src/main/java/cz/jzitnik/game/mobs/EntityKillInterface.java +++ b/src/main/java/cz/jzitnik/game/mobs/EntityKillInterface.java @@ -4,5 +4,5 @@ import cz.jzitnik.game.Game; import cz.jzitnik.game.entities.Block; public interface EntityKillInterface { - void killed(Game game, Block mob); + void killed(Game game, Block mob, int x, int y); } diff --git a/src/main/java/cz/jzitnik/game/mobs/services/cow/CowLogic.java b/src/main/java/cz/jzitnik/game/mobs/services/cow/CowLogic.java index b461ae8..b7a790c 100644 --- a/src/main/java/cz/jzitnik/game/mobs/services/cow/CowLogic.java +++ b/src/main/java/cz/jzitnik/game/mobs/services/cow/CowLogic.java @@ -199,7 +199,7 @@ public class CowLogic } @Override - public void killed(Game game, Block mob) { + public void killed(Game game, Block mob, int x, int y) { int leatherAmount = random.nextInt(3); InventoryItem inventoryItem = new InventoryItem(leatherAmount, ItemBlockSupplier.getItem("leather")); int beefAmount = random.nextInt(3) + 1; diff --git a/src/main/java/cz/jzitnik/game/mobs/services/pig/PigLogic.java b/src/main/java/cz/jzitnik/game/mobs/services/pig/PigLogic.java index 55662c2..0dcbac2 100644 --- a/src/main/java/cz/jzitnik/game/mobs/services/pig/PigLogic.java +++ b/src/main/java/cz/jzitnik/game/mobs/services/pig/PigLogic.java @@ -199,7 +199,7 @@ public class PigLogic } @Override - public void killed(Game game, Block mob) { + public void killed(Game game, Block mob, int x, int y) { int amount = random.nextInt(2) + 1; InventoryItem inventoryItem = new InventoryItem(amount, ItemBlockSupplier.getItem("porkchop")); game.getInventory().addItem(inventoryItem); diff --git a/src/main/java/cz/jzitnik/game/mobs/services/sheep/SheepLogic.java b/src/main/java/cz/jzitnik/game/mobs/services/sheep/SheepLogic.java index b3df3b5..8d34bbd 100644 --- a/src/main/java/cz/jzitnik/game/mobs/services/sheep/SheepLogic.java +++ b/src/main/java/cz/jzitnik/game/mobs/services/sheep/SheepLogic.java @@ -242,7 +242,7 @@ public class SheepLogic } @Override - public void killed(Game game, Block mob) { + public void killed(Game game, Block mob, int x, int y) { int amount = random.nextInt(3) + 1; var sheepData = (SheepData) mob.getData(); diff --git a/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieHurtKillLogic.java b/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieHurtKillLogic.java new file mode 100644 index 0000000..32dd894 --- /dev/null +++ b/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieHurtKillLogic.java @@ -0,0 +1,49 @@ +package cz.jzitnik.game.mobs.services.zombie; + +import cz.jzitnik.game.Game; +import cz.jzitnik.game.annotations.EntityHurtAnimationHandler; +import cz.jzitnik.game.annotations.EntityKillHandler; +import cz.jzitnik.game.entities.Block; +import cz.jzitnik.game.mobs.*; +import cz.jzitnik.game.sprites.Zombie; + +import java.util.*; + +import static cz.jzitnik.game.sprites.Zombie.ZombieState.*; + +@EntityHurtAnimationHandler("zombie") +@EntityKillHandler("zombie") +public class ZombieHurtKillLogic implements EntityHurtAnimationChanger, EntityKillInterface { + private final Random random = new Random(); + + public Zombie.ZombieState setHurtAnimation(boolean hurt, Enum current) { + if (hurt) { + return switch (current) { + case TOP, TOP_HURT -> TOP_HURT; + case BOTTOM, BOTTOM_HURT -> BOTTOM_HURT; + default -> throw new IllegalStateException("Unexpected state: " + current); + }; + } + + return switch (current) { + case TOP, TOP_HURT -> TOP; + case BOTTOM, BOTTOM_HURT -> BOTTOM; + default -> throw new IllegalStateException("Unexpected state: " + current); + }; + } + + @Override + public void killed(Game game, Block mob, int x, int y) { + int rottenFlesh = random.nextInt(3) + 1; + + boolean isTop = mob.getSpriteState().isPresent() && (mob.getSpriteState().get().equals(TOP_HURT) || mob.getSpriteState().get().equals(TOP)); + int newY = isTop ? y + 1 : y - 1; + + game.getWorld()[newY][x].remove(mob.getLinkedMobTexture()); + + // Rotten flesh is currently not an item. So this is commented out + // TODO: Add rotten flesh to the game + //InventoryItem drop = new InventoryItem(rottenFlesh, ItemBlockSupplier.getItem("rotten_flesh")); + //game.getInventory().addItem(drop); + } +} \ No newline at end of file diff --git a/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieMoveLogic.java b/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieMoveLogic.java new file mode 100644 index 0000000..8112e1e --- /dev/null +++ b/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieMoveLogic.java @@ -0,0 +1,13 @@ +package cz.jzitnik.game.mobs.services.zombie; + +import cz.jzitnik.game.annotations.EntityLogic; +import cz.jzitnik.game.mobs.EntityLogicInterface; +import cz.jzitnik.game.mobs.EntityLogicProvider; + +@EntityLogic("zombie") +public class ZombieMoveLogic implements EntityLogicInterface { + @Override + public void nextIteration(EntityLogicProvider.EntityLogicMobDTO entityLogicMobDTO) { + + } +} diff --git a/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieLogic.java b/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieSpawnLogic.java similarity index 59% rename from src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieLogic.java rename to src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieSpawnLogic.java index 8b13715..3a17c5f 100644 --- a/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieLogic.java +++ b/src/main/java/cz/jzitnik/game/mobs/services/zombie/ZombieSpawnLogic.java @@ -1,49 +1,30 @@ package cz.jzitnik.game.mobs.services.zombie; 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.*; +import cz.jzitnik.game.mobs.EntitySpawnInterface; import cz.jzitnik.game.sprites.Zombie; -import cz.jzitnik.game.sprites.Zombie.ZombieState; import cz.jzitnik.tui.ScreenMovingCalculationProvider; import lombok.extern.slf4j.Slf4j; import org.jline.terminal.Terminal; import java.util.*; - -import static cz.jzitnik.game.sprites.Zombie.ZombieState.*; - -@Slf4j @EntitySpawn -@EntityLogic("zombie") -@EntityHurtAnimationHandler("zombie") -@EntityKillHandler("zombie") -public class ZombieLogic - implements EntityLogicInterface, EntitySpawnInterface, EntityHurtAnimationChanger, EntityKillInterface { - +public class ZombieSpawnLogic implements EntitySpawnInterface { private final Random random = new Random(); - @Override - public void nextIteration(EntityLogicProvider.EntityLogicMobDTO entityLogicMobDTO) { - log.debug("Running zombie logic"); - } - @Override public void spawn(int playerX, int playerY, Game game, Terminal terminal) { int[] view = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length); int startX = view[0]; int endX = view[1]; - //attemptZombieSpawn(startX - 20, startX - 5, playerY, game); - //attemptZombieSpawn(endX + 5, endX + 20, playerY, game); + attemptZombieSpawn(startX - 20, startX - 5, playerY, game); + attemptZombieSpawn(endX + 5, endX + 20, playerY, game); } + private void attemptZombieSpawn(int startX, int endX, int centerY, Game game) { if (countZombies(startX, endX, centerY - 15, centerY + 15, game) < 3 && random.nextInt(100) < 2) { var spawnLocations = zombieCanSpawn(startX, endX, centerY, game); @@ -54,10 +35,13 @@ public class ZombieLogic int y = loc.getValue(); var top = ItemBlockSupplier.getEntity("zombie"); - top.setSpriteState(ZombieState.TOP); + top.setSpriteState(Zombie.ZombieState.TOP); var bottom = ItemBlockSupplier.getEntity("zombie"); - bottom.setSpriteState(ZombieState.BOTTOM); + bottom.setSpriteState(Zombie.ZombieState.BOTTOM); + + top.setLinkedMobTexture(bottom); + bottom.setLinkedMobTexture(top); game.getWorld()[y - 1][x].add(top); game.getWorld()[y][x].add(bottom); @@ -66,6 +50,7 @@ public class ZombieLogic } } + private HashMap zombieCanSpawn(int startX, int endX, int playerY, Game game) { var map = new HashMap(); var world = game.getWorld(); @@ -89,29 +74,6 @@ public class ZombieLogic return count / 2; // Each zombie has two parts } - public Zombie.ZombieState setHurtAnimation(boolean hurt, Enum current) { - if (hurt) { - return switch (current) { - case TOP, TOP_HURT -> TOP_HURT; - case BOTTOM, BOTTOM_HURT -> BOTTOM_HURT; - default -> throw new IllegalStateException("Unexpected state: " + current); - }; - } - - return switch (current) { - case TOP, TOP_HURT -> TOP; - case BOTTOM, BOTTOM_HURT -> BOTTOM; - default -> throw new IllegalStateException("Unexpected state: " + current); - }; - } - - @Override - public void killed(Game game, Block mob) { - int rottenFlesh = random.nextInt(3) + 1; - //InventoryItem drop = new InventoryItem(rottenFlesh, ItemBlockSupplier.getItem("rotten_flesh")); - //game.getInventory().addItem(drop); - } - public static Map.Entry getRandomEntry(HashMap map) { List> list = new ArrayList<>(map.entrySet()); return list.get(new Random().nextInt(list.size()));