refactor: Load a game setup from a yaml file

This commit is contained in:
2026-01-24 21:08:43 +01:00
parent aac651cf93
commit 743ad8e760
32 changed files with 455 additions and 304 deletions

View File

@@ -6,6 +6,6 @@ import lombok.Getter;
@Getter
@Config
public class Debugging {
private final boolean renderColliders = true;
private final boolean renderPlayerCollider = true;
private final boolean renderColliders = false;
private final boolean renderPlayerCollider = false;
}

View File

@@ -1,8 +1,9 @@
package cz.jzitnik.game;
import com.fasterxml.jackson.annotation.*;
import cz.jzitnik.game.mobs.Mob;
import cz.jzitnik.game.objects.DroppedItem;
import cz.jzitnik.game.objects.GameObject;
import cz.jzitnik.game.mobs.Mob;
import cz.jzitnik.ui.pixels.Empty;
import cz.jzitnik.ui.pixels.Pixel;
import lombok.Getter;
@@ -12,23 +13,46 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
@Getter
public class GameRoom {
private final String id;
@JsonIgnore
private final Pixel[][] overrideBuffer;
@JsonIgnore
private final ResourceManager.Resource texture;
@JsonIgnore
private final List<GameObject> objects = new ArrayList<>();
@JsonIgnore
private final List<Mob> mobs = new ArrayList<>();
@JsonIgnore
private final Set<DroppedItem> droppedItems = new HashSet<>();
@JsonIgnore
private final List<GameRoomPart> colliders = new ArrayList<>();
private GameRoom left;
private GameRoom right;
private GameRoom up;
private GameRoom down;
private final Pixel[][] overrideBuffer;
private final ResourceManager.Resource texture;
private final List<GameObject> objects = new ArrayList<>();
private final List<Mob> mobs = new ArrayList<>();
private final Set<DroppedItem> droppedItems = new HashSet<>();
private final List<GameRoomPart> colliders = new ArrayList<>();
public GameRoom(ResourceManager.Resource texture) {
@JsonCreator
public GameRoom(
@JsonProperty("id") String id,
@JsonProperty("objects") List<GameObject> objects,
@JsonProperty("colliders") List<GameRoomPart> colliders,
@JsonProperty("mobs") List<Mob> mobs,
@JsonProperty("texture") ResourceManager.Resource texture
) {
this.id = id;
this.texture = texture;
// Size of a room
if (objects != null) this.objects.addAll(objects);
if (colliders != null) this.colliders.addAll(colliders);
if (mobs != null) this.mobs.addAll(mobs);
int height = 225;
int width = 225 * 2;
@@ -38,56 +62,26 @@ public class GameRoom {
overrideBuffer[y][x] = new Empty();
}
}
this.overrideBuffer = overrideBuffer;
}
public void addObject(GameObject gameObject) {
objects.add(gameObject);
@JsonSetter("west")
public void setWest(GameRoom west) {
if (west != null) this.left = west;
}
public void addMob(Mob mob) {
mobs.add(mob);
@JsonSetter("east")
public void setEast(GameRoom east) {
if (east != null) this.right = east;
}
public void addCollider(GameRoomPart collider) {
colliders.add(collider);
@JsonSetter("north")
public void setNorth(GameRoom north) {
if (north != null) this.up = north;
}
public void setLeft(GameRoom left) {
setLeft(left, true);
}
protected void setLeft(GameRoom left, boolean addReference) {
this.left = left;
if (addReference) {
left.setRight(this, false);
}
}
public void setRight(GameRoom right) {
setRight(right, true);
}
protected void setRight(GameRoom right, boolean addReference) {
this.right = right;
if (addReference) {
right.setLeft(this, false);
}
}
public void setUp(GameRoom up) {
setUp(up, true);
}
protected void setUp(GameRoom up, boolean addReference) {
this.up = up;
if (addReference) {
up.setDown(this, false);
}
}
public void setDown(GameRoom down) {
setDown(down, true);
}
protected void setDown(GameRoom down, boolean addReference) {
this.down = down;
if (addReference) {
down.setUp(this, false);
}
@JsonSetter("south")
public void setSouth(GameRoom south) {
if (south != null) this.down = south;
}
}

View File

@@ -1,17 +1,27 @@
package cz.jzitnik.game;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.game.utils.RoomCords;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
@Data
@AllArgsConstructor
@ToString
public class GameRoomPart {
private RoomCords start;
private RoomCords end;
@JsonCreator
public GameRoomPart(
@JsonProperty("start") RoomCords start,
@JsonProperty("end") RoomCords end
) {
this.start = start;
this.end = end;
}
public boolean isWithin(RoomCords cords) {
return cords.getX() >= start.getX() &&
cords.getX() <= end.getX() &&

View File

@@ -1,7 +1,6 @@
package cz.jzitnik.game;
import cz.jzitnik.annotations.State;
import cz.jzitnik.game.dialog.Dialog;
import cz.jzitnik.game.objects.Interactable;
import cz.jzitnik.screens.Screen;
import cz.jzitnik.utils.DependencyManager;

View File

@@ -1,5 +1,7 @@
package cz.jzitnik.game;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.events.RerenderPart;
import cz.jzitnik.game.items.GameItem;
import cz.jzitnik.game.items.types.interfaces.WeaponInterface;
@@ -10,7 +12,6 @@ import cz.jzitnik.utils.DependencyManager;
import cz.jzitnik.utils.events.Event;
import cz.jzitnik.utils.events.EventManager;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -20,7 +21,6 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor
@Getter
@Slf4j
public class Player {
@@ -41,6 +41,15 @@ public class Player {
private boolean hitAnimationOn = false;
private ScheduledFuture<?> currentTimeoutHitAnimation = null;
@JsonCreator
public Player(
@JsonProperty("playerCords") RoomCords playerCords,
@JsonProperty("collider") GameRoomPart collider
) {
this.playerCords = playerCords;
this.collider = collider;
}
public void increaseStamina() {
stamina++;
}

View File

@@ -1,16 +1,29 @@
package cz.jzitnik.game.items;
import com.fasterxml.jackson.annotation.*;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.game.items.types.ItemType;
import cz.jzitnik.game.utils.Renderable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.awt.image.BufferedImage;
@Getter
@AllArgsConstructor
public abstract class GameItem implements Renderable {
private String name;
public class GameItem implements Renderable {
private final ItemType<?> type;
@JsonIgnore
private final BufferedImage texture;
private final String name;
@JsonCreator
public GameItem(
@JsonProperty("name") String name,
@JsonProperty("type") ItemType<?> type,
@JsonProperty("texture") ResourceManager.Resource resource,
@JacksonInject ResourceManager resourceManager
) {
this.name = name;
this.type = type;
this.texture = resourceManager.getResource(resource);
}
}

View File

@@ -1,5 +1,18 @@
package cz.jzitnik.game.items.types;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import cz.jzitnik.game.items.types.food.Food;
import cz.jzitnik.game.items.types.weapons.Sword;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "name"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Food.class, name = "food"),
@JsonSubTypes.Type(value = Sword.class, name = "weapon_sword")
})
public interface ItemType<T> {
Class<T> getItemType();
}

View File

@@ -1,5 +1,7 @@
package cz.jzitnik.game.items.types.food;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.events.RenderStats;
import cz.jzitnik.game.GameState;
import cz.jzitnik.game.items.types.InteractableItem;
@@ -7,12 +9,17 @@ import cz.jzitnik.game.items.types.ItemType;
import cz.jzitnik.utils.DependencyManager;
import cz.jzitnik.utils.StateManager;
import cz.jzitnik.utils.events.EventManager;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Food implements InteractableItem, ItemType<Food> {
private final int addHealth;
@JsonCreator
public Food(
@JsonProperty("addHealth") int addHealth
) {
this.addHealth = addHealth;
}
@Override
public InteractableItemResponse interact(DependencyManager dependencyManager, StateManager stateManager) {
GameState gameState = stateManager.getOrThrow(GameState.class);

View File

@@ -1,7 +1,13 @@
package cz.jzitnik.game.items.types.weapons;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Sword extends Weapon {
public Sword(int damageDeal) {
super(damageDeal);
@JsonCreator
public Sword(
@JsonProperty("dealDamage") int dealDamage
) {
super(dealDamage);
}
}

View File

@@ -3,16 +3,18 @@ package cz.jzitnik.game.items.types.weapons;
import cz.jzitnik.game.items.types.ItemType;
import cz.jzitnik.game.items.types.interfaces.WeaponInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public abstract class Weapon implements ItemType<Weapon>, WeaponInterface {
protected int damageDeal;
private final int dealDamage;
@Override
public final Class<Weapon> getItemType() {
return Weapon.class;
}
@Override
public int getDamageDeal() {
return dealDamage;
}
}

View File

@@ -4,11 +4,10 @@ import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.game.GameRoomPart;
import cz.jzitnik.game.dialog.Dialog;
import cz.jzitnik.game.mobs.tasks.MobRoomTask;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.states.DialogState;
import cz.jzitnik.utils.events.EventManager;
import cz.jzitnik.utils.roomtasks.RoomTask;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
@@ -17,16 +16,11 @@ import java.awt.image.BufferedImage;
public abstract class DialogMob extends Mob {
protected Dialog dialog;
public DialogMob(BufferedImage texture, RoomTask[] tasks, RoomCords cords, GameRoomPart collider, Dialog dialog) {
public DialogMob(BufferedImage texture, MobRoomTask[] tasks, RoomCords cords, GameRoomPart collider, Dialog dialog) {
super(texture, tasks, cords, collider);
this.dialog = dialog;
}
public DialogMob(BufferedImage texture, RoomTask task, RoomCords cords, GameRoomPart collider, Dialog dialog) {
super(texture, task, cords, collider);
this.dialog = dialog;
}
@InjectDependency
private EventManager eventManager;

View File

@@ -5,8 +5,8 @@ import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.RerenderPart;
import cz.jzitnik.game.GameRoomPart;
import cz.jzitnik.game.GameState;
import cz.jzitnik.game.mobs.tasks.MobRoomTask;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.utils.DependencyManager;
import cz.jzitnik.utils.events.EventManager;
import cz.jzitnik.utils.roomtasks.RoomTask;
import cz.jzitnik.utils.roomtasks.RoomTaskScheduler;
@@ -68,9 +68,9 @@ public abstract class HittableMob extends Mob {
@InjectDependency
private RoomTaskScheduler roomTaskScheduler;
public HittableMob(BufferedImage texture, RoomTask task, RoomCords cords, GameRoomPart collider, int initialHealth) {
super(texture, task, cords, collider);
health = initialHealth;
public HittableMob(BufferedImage texture, MobRoomTask[] tasks, RoomCords cords, GameRoomPart collider, int health) {
super(texture, tasks, cords, collider);
this.health = health;
}
@Override

View File

@@ -1,49 +1,47 @@
package cz.jzitnik.game.mobs;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.DroppedItemRerender;
import cz.jzitnik.events.InventoryRerender;
import cz.jzitnik.game.GameRoom;
import cz.jzitnik.game.GameRoomPart;
import cz.jzitnik.game.GameState;
import cz.jzitnik.game.Player;
import cz.jzitnik.game.*;
import cz.jzitnik.game.items.GameItem;
import cz.jzitnik.game.mobs.tasks.MobRoomTask;
import cz.jzitnik.game.objects.DroppedItem;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.utils.events.Event;
import cz.jzitnik.utils.events.EventManager;
import cz.jzitnik.utils.roomtasks.RoomTask;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Supplier;
public abstract class HittableMobDrops extends HittableMob {
public class HittableMobDrops extends HittableMob {
private static final int DROP_ITEM_ON_GROUND_RADIUS = 30;
private final GameItem[] itemsDrops;
@InjectState
private GameState gameState;
@InjectDependency
private EventManager eventManager;
private final Supplier<GameItem[]> itemDropSupplier;
public HittableMobDrops(BufferedImage texture, RoomTask task, RoomCords cords, GameRoomPart collider, int initialHealth, Supplier<GameItem[]> itemDropSupplier) {
super(texture, task, cords, collider, initialHealth);
this.itemDropSupplier = itemDropSupplier;
@JsonCreator
public HittableMobDrops(
@JsonProperty("texture") ResourceManager.Resource texture,
@JsonProperty("tasks") MobRoomTask[] tasks,
@JsonProperty("cords") RoomCords cords,
@JsonProperty("collider") GameRoomPart collider,
@JsonProperty("health") int health,
@JsonProperty("itemsDrops") GameItem[] itemsDrops,
@JacksonInject ResourceManager resourceManager
) {
super(resourceManager.getResource(texture), tasks, cords, collider, health);
this.itemsDrops = itemsDrops == null ? new GameItem[]{} : itemsDrops;
}
public HittableMobDrops(Supplier<GameItem> itemDropSupplier, BufferedImage texture, RoomTask task, RoomCords cords, GameRoomPart collider, int initialHealth) {
super(texture, task, cords, collider, initialHealth);
this.itemDropSupplier = () -> new GameItem[]{
itemDropSupplier.get()
};
}
private static final int DROP_ITEM_ON_GROUND_RADIUS = 30;
/**
* Can be overwritten by an extending class
**/
@@ -52,7 +50,6 @@ public abstract class HittableMobDrops extends HittableMob {
@Override
public final void onKilled() {
GameItem[] items = itemDropSupplier.get();
boolean addedIntoInventory = false;
Player player = gameState.getPlayer();
RoomCords enemyCords = getCords();
@@ -64,7 +61,7 @@ public abstract class HittableMobDrops extends HittableMob {
List<Event> events = new ArrayList<>();
for (GameItem item : items) {
for (GameItem item : itemsDrops) {
boolean added = player.addItem(item);
if (added) {

View File

@@ -1,14 +1,24 @@
package cz.jzitnik.game.mobs;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.game.GameRoomPart;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.game.mobs.tasks.MobRoomTask;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.utils.roomtasks.RoomTask;
import java.awt.image.BufferedImage;
public abstract class HittableMobNoDrops extends HittableMob {
public HittableMobNoDrops(BufferedImage texture, RoomTask task, RoomCords cords, GameRoomPart collider, int initialHealth) {
super(texture, task, cords, collider, initialHealth);
public class HittableMobNoDrops extends HittableMob {
@JsonCreator
public HittableMobNoDrops(
@JsonProperty("texture") ResourceManager.Resource texture,
@JsonProperty("tasks") MobRoomTask[] tasks,
@JsonProperty("cords") RoomCords cords,
@JsonProperty("collider") GameRoomPart collider,
@JsonProperty("health") int health,
@JacksonInject ResourceManager resourceManager
) {
super(resourceManager.getResource(texture), tasks, cords, collider, health);
}
@Override

View File

@@ -1,47 +1,62 @@
package cz.jzitnik.game.mobs;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.game.GameRoomPart;
import cz.jzitnik.game.mobs.tasks.MobRoomTask;
import cz.jzitnik.game.utils.Renderable;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.game.utils.Selectable;
import cz.jzitnik.utils.roomtasks.RoomTask;
import cz.jzitnik.utils.roomtasks.RoomTaskScheduler;
import lombok.Getter;
import lombok.Setter;
import java.awt.image.BufferedImage;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = DialogMob.class, name = "dialog"),
@JsonSubTypes.Type(value = HittableMobDrops.class, name = "hittable_drops"),
@JsonSubTypes.Type(value = HittableMobNoDrops.class, name = "hittable_no_drops")
})
@Getter
public abstract class Mob implements Renderable, Selectable {
@JsonIgnore
protected final BufferedImage texture;
protected RoomTask[] tasks;
@JsonIgnore
protected MobRoomTask[] tasks;
@JsonIgnore
protected final RoomCords cords;
@JsonIgnore
protected final GameRoomPart collider;
@InjectDependency
private RoomTaskScheduler roomTaskScheduler;
protected void updateTasks(RoomTask[] tasks) {
protected void updateTasks(MobRoomTask[] tasks) {
var oldTasks = this.tasks;
this.tasks = tasks;
roomTaskScheduler.registerNewMob(this, oldTasks);
}
public Mob(BufferedImage texture, RoomTask task, RoomCords cords, GameRoomPart collider) {
public Mob(BufferedImage texture, MobRoomTask[] tasks, RoomCords cords, GameRoomPart collider) {
this.texture = texture;
this.tasks = new RoomTask[] {task};
this.tasks = tasks == null ? new MobRoomTask[] {} : tasks;
this.cords = cords;
this.collider = collider;
}
public Mob(BufferedImage texture, RoomTask[] tasks, RoomCords cords, GameRoomPart collider) {
this.texture = texture;
this.tasks = tasks;
this.cords = cords;
this.collider = collider;
if (tasks != null) {
for (MobRoomTask task : tasks) {
task.setOwner(this);
}
}
}
@Setter

View File

@@ -1,5 +1,7 @@
package cz.jzitnik.game.mobs.tasks;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.annotations.injectors.InjectConfig;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
@@ -13,19 +15,33 @@ import cz.jzitnik.states.MicrophoneState;
import cz.jzitnik.states.ScreenBuffer;
import cz.jzitnik.states.TerminalState;
import cz.jzitnik.utils.events.EventManager;
import cz.jzitnik.utils.roomtasks.RoomTask;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.concurrent.TimeUnit;
public class BlindMobFollowingPlayerTask extends RoomTask {
public BlindMobFollowingPlayerTask(Mob mob, int speed, int updateRateMs) {
super(new Task(mob, speed), updateRateMs, TimeUnit.MILLISECONDS);
public class BlindMobFollowingPlayerTask extends MobRoomTask {
private final Task task;
@JsonCreator
public BlindMobFollowingPlayerTask(
@JsonProperty("speed") int speed,
@JsonProperty("updateRateMs") int updateRateMs
) {
Task task = new Task(speed);
super(task, updateRateMs, TimeUnit.MILLISECONDS);
this.task = task;
}
@Override
public void setOwner(Mob mob) {
task.setMob(mob);
}
@RequiredArgsConstructor
private static class Task implements Runnable {
private final Mob mob;
@Setter
private Mob mob;
private final int speed;
private RoomCords playerCords;
@InjectState

View File

@@ -1,5 +1,7 @@
package cz.jzitnik.game.mobs.tasks;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.RenderStats;
@@ -8,24 +10,38 @@ import cz.jzitnik.game.mobs.Mob;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.utils.DependencyManager;
import cz.jzitnik.utils.events.EventManager;
import cz.jzitnik.utils.roomtasks.RoomTask;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@Slf4j
public class EnemyPlayerHittingTask extends RoomTask {
public EnemyPlayerHittingTask(Mob mob, long updateRateMs, double reach, Supplier<Integer> damageSupplier) {
super(new Task(reach, damageSupplier, mob), updateRateMs, TimeUnit.MILLISECONDS);
public class EnemyPlayerAttackingTask extends MobRoomTask {
private final Task task;
@JsonCreator
public EnemyPlayerAttackingTask(
@JsonProperty("updateRateMs") long updateRateMs,
@JsonProperty("reach") double reach,
@JsonProperty("damage") int damage
) {
Task task = new Task(reach, damage);
super(task, updateRateMs, TimeUnit.MILLISECONDS);
this.task = task;
}
@Override
public void setOwner(Mob mob) {
task.setMob(mob);
}
@RequiredArgsConstructor
private static class Task implements Runnable {
private final double reach;
private final Supplier<Integer> damageSupplier;
private final Mob mob;
private final int damage;
@Setter
private Mob mob;
@InjectState
private GameState gameState;
@@ -46,8 +62,6 @@ public class EnemyPlayerHittingTask extends RoomTask {
return;
}
int damage = damageSupplier.get();
boolean isDead = gameState.getPlayer().dealDamage(damage, dependencyManager);
eventManager.emitEvent(new RenderStats());

View File

@@ -1,5 +1,7 @@
package cz.jzitnik.game.mobs.tasks;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.googlecode.lanterna.TerminalPosition;
import cz.jzitnik.annotations.injectors.InjectConfig;
import cz.jzitnik.annotations.injectors.InjectDependency;
@@ -19,8 +21,8 @@ import cz.jzitnik.states.TerminalState;
import cz.jzitnik.utils.RerenderUtils;
import cz.jzitnik.utils.events.Event;
import cz.jzitnik.utils.events.EventManager;
import cz.jzitnik.utils.roomtasks.RoomTask;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.awt.image.BufferedImage;
@@ -28,16 +30,29 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
public class MobFollowingPlayerTask extends RoomTask {
public MobFollowingPlayerTask(Mob mob, int speed, int updateRateMs) {
super(new Task(mob, speed), updateRateMs, TimeUnit.MILLISECONDS);
public class MobFollowingPlayerTask extends MobRoomTask {
private final Task task;
@JsonCreator
public MobFollowingPlayerTask(
@JsonProperty("speed") int speed,
@JsonProperty("updateRateMs") int updateRateMs
) {
Task task = new Task(speed);
super(task, updateRateMs, TimeUnit.MILLISECONDS);
this.task = task;
}
@Override
public void setOwner(Mob mob) {
task.setMob(mob);
}
@RequiredArgsConstructor
static class Task implements Runnable {
private final Mob mob;
private final int speed;
@Setter
private Mob mob;
@InjectState
private GameState gameState;

View File

@@ -0,0 +1,26 @@
package cz.jzitnik.game.mobs.tasks;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import cz.jzitnik.game.mobs.Mob;
import cz.jzitnik.utils.roomtasks.RoomTask;
import java.util.concurrent.TimeUnit;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "type"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = BlindMobFollowingPlayerTask.class, name = "blind_following_player"),
@JsonSubTypes.Type(value = MobFollowingPlayerTask.class, name = "following_player"),
@JsonSubTypes.Type(value = EnemyPlayerAttackingTask.class, name = "attacking_player")
})
public abstract class MobRoomTask extends RoomTask {
public MobRoomTask(Runnable task, long rate, TimeUnit rateUnit) {
super(task, rate, rateUnit);
}
public abstract void setOwner(Mob mob);
}

View File

@@ -1,5 +1,8 @@
package cz.jzitnik.game.objects;
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Lists;
import com.googlecode.lanterna.TerminalPosition;
import com.googlecode.lanterna.TextColor;
@@ -66,9 +69,14 @@ public final class Chest extends GameObject implements UIClickHandler {
@InjectConfig
private Debugging debugging;
public Chest(ResourceManager resourceManager, RoomCords cords, GameItem[] items) {
@JsonCreator
public Chest(
@JsonProperty("cords") RoomCords cords,
@JsonProperty("items") GameItem[] items,
@JacksonInject ResourceManager resourceManager
) {
super(resourceManager.getResource(ResourceManager.Resource.CHEST), cords, true);
this.items = Lists.newArrayList(items);
this.items = Lists.newArrayList(items == null ? new GameItem[] {} : items);
}
@Override

View File

@@ -1,5 +1,8 @@
package cz.jzitnik.game.objects;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import cz.jzitnik.game.utils.Renderable;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.game.utils.Selectable;
@@ -11,11 +14,22 @@ import java.awt.image.BufferedImage;
@Getter
@RequiredArgsConstructor
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
property = "objectType"
)
@JsonSubTypes({
@JsonSubTypes.Type(value = Chest.class, name = "chest")
})
public sealed abstract class GameObject implements Renderable, Selectable permits Chest {
@JsonIgnore
private final BufferedImage texture;
@JsonIgnore
private final RoomCords cords;
@JsonIgnore
private final boolean selectable;
@JsonIgnore
@Setter
private boolean selected = false;
}

View File

@@ -1,13 +1,22 @@
package cz.jzitnik.game.setup;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import cz.jzitnik.annotations.Dependency;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.game.*;
import cz.jzitnik.game.setup.rooms.MainRoom;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.game.GameRoom;
import cz.jzitnik.game.GameState;
import cz.jzitnik.game.Player;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.utils.DependencyManager;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.List;
@Slf4j
@Dependency
public class GameSetup {
@InjectState
@@ -19,28 +28,19 @@ public class GameSetup {
@InjectDependency
private DependencyManager dependencyManager;
public void setup() {
public void setup() throws IOException {
//gameState.setScreen(new IntroScene(dependencyManager));
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
mapper.setInjectableValues(dependencyManager);
GameRoom mainRoom = new MainRoom(resourceManager);
GameRoom rightRoom = new GameRoom(ResourceManager.Resource.ROOM2);
GameRoom topRightRoom = new GameRoom(ResourceManager.Resource.ROOM3);
GameRoom topRightTop = new GameRoom(ResourceManager.Resource.ROOM4);
GameRoom topRightTopLeft = new GameRoom(ResourceManager.Resource.ROOM_FROZEN);
List<GameRoom> rooms = mapper.readValue(
resourceManager.getResourceAsStream("setup/rooms.yaml"),
new TypeReference<>() {
}
);
Player player = mapper.readValue(resourceManager.getResourceAsStream("setup/player.yaml"), Player.class);
mainRoom.setRight(rightRoom);
rightRoom.setUp(topRightRoom);
topRightRoom.setUp(topRightTop);
topRightTop.setLeft(topRightTopLeft);
gameState.setCurrentRoom(mainRoom);
gameState.setPlayer(new Player(
new RoomCords(90, 100),
new GameRoomPart(
new RoomCords(0, 52),
new RoomCords(44, 78)
)
));
gameState.setCurrentRoom(rooms.getFirst());
gameState.setPlayer(player);
}
}

View File

@@ -1,43 +0,0 @@
package cz.jzitnik.game.setup.enemies;
import cz.jzitnik.game.GameRoomPart;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.game.dialog.Dialog;
import cz.jzitnik.game.dialog.OnEnd;
import cz.jzitnik.game.mobs.DialogMob;
import cz.jzitnik.game.mobs.tasks.EnemyPlayerHittingTask;
import cz.jzitnik.game.mobs.tasks.MobFollowingPlayerTask;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.utils.roomtasks.RoomTask;
public class Pepa extends DialogMob {
public Pepa(ResourceManager resourceManager, RoomCords cords) {
super(resourceManager.getResource(ResourceManager.Resource.PLAYER_FRONT), new RoomTask[]{}, cords,
new GameRoomPart(
new RoomCords(0, 52),
new RoomCords(44, 78)
),
null
);
dialog = new Dialog(
"Pepa: Never gonna give you up",
new OnEnd.Continue(new Dialog(
"Pepa: Never gonna let you down",
new OnEnd.Continue(new Dialog(
"Pepa: Never gonna run around",
new OnEnd.Continue(new Dialog(
"Pepa: How it continues?", new OnEnd.AskQuestion(
new OnEnd.AskQuestion.Answer[]{
new OnEnd.AskQuestion.Answer("And desert you", new Dialog("Pepa: You are god damn right!", new OnEnd.Continue(null))),
new OnEnd.AskQuestion.Answer("You are a dessert", new Dialog("Pepa: WRONG!", new OnEnd.RunCode(() -> updateTasks(new RoomTask[]{
new MobFollowingPlayerTask(this, 1, 100),
new EnemyPlayerHittingTask(this, 500, 15, () -> 5)
}), null)))
}
)))
))
))
);
}
}

View File

@@ -1,28 +0,0 @@
package cz.jzitnik.game.setup.enemies;
import cz.jzitnik.game.GameRoomPart;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.game.mobs.HittableMobDrops;
import cz.jzitnik.game.mobs.tasks.EnemyPlayerHittingTask;
import cz.jzitnik.game.mobs.tasks.MobFollowingPlayerTask;
import cz.jzitnik.game.setup.items.Apple;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.utils.roomtasks.RoomTask;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Zombie extends HittableMobDrops {
public Zombie(ResourceManager resourceManager, RoomCords cords) {
super(() -> new Apple(resourceManager), resourceManager.getResource(ResourceManager.Resource.PLAYER_FRONT), null, cords,
new GameRoomPart(
new RoomCords(0, 52),
new RoomCords(44, 78)
),
10
);
tasks = new RoomTask[]{
new MobFollowingPlayerTask(this, 1, 100),
new EnemyPlayerHittingTask(this, 500, 15, () -> 5)
};
}
}

View File

@@ -1,15 +0,0 @@
package cz.jzitnik.game.setup.items;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.game.items.GameItem;
import cz.jzitnik.game.items.types.food.Food;
public class Apple extends GameItem {
public Apple(ResourceManager resourceManager) {
super(
"Apple",
new Food(1),
resourceManager.getResource(ResourceManager.Resource.APPLE)
);
}
}

View File

@@ -1,15 +0,0 @@
package cz.jzitnik.game.setup.items;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.game.items.GameItem;
import cz.jzitnik.game.items.types.weapons.Sword;
public class WoodenSword extends GameItem {
public WoodenSword(ResourceManager resourceManager) {
super(
"Wooden sword",
new Sword(2),
resourceManager.getResource(ResourceManager.Resource.WOODEN_SWORD)
);
}
}

View File

@@ -1,35 +0,0 @@
package cz.jzitnik.game.setup.rooms;
import cz.jzitnik.game.GameRoom;
import cz.jzitnik.game.GameRoomPart;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.game.items.GameItem;
import cz.jzitnik.game.setup.enemies.Pepa;
import cz.jzitnik.game.setup.items.Apple;
import cz.jzitnik.game.setup.items.WoodenSword;
import cz.jzitnik.game.objects.Chest;
import cz.jzitnik.game.setup.enemies.Zombie;
import cz.jzitnik.game.utils.RoomCords;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MainRoom extends GameRoom {
public MainRoom(ResourceManager resourceManager) {
super(ResourceManager.Resource.ROOM1);
Chest chest = new Chest(resourceManager, new RoomCords(100, 45), new GameItem[]{
new WoodenSword(resourceManager),
new Apple(resourceManager)
});
addCollider(new GameRoomPart(
new RoomCords(100, 45),
new RoomCords(140, 67)
));
addObject(chest);
//Zombie zombie = new Zombie(resourceManager, new RoomCords(100, 100));
//addMob(zombie);
Pepa pepa = new Pepa(resourceManager, new RoomCords(100, 100));
addMob(pepa);
}
}

View File

@@ -1,5 +1,7 @@
package cz.jzitnik.game.utils;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import cz.jzitnik.game.GameRoomPart;
import lombok.Getter;
import lombok.ToString;
@@ -14,7 +16,11 @@ public class RoomCords implements Cloneable {
private int x;
private int y;
public RoomCords(int x, int y) {
@JsonCreator
public RoomCords(
@JsonProperty("x") int x,
@JsonProperty("y") int y
) {
updateCords(x, y);
}
@@ -46,6 +52,7 @@ public class RoomCords implements Cloneable {
/**
* Calculates the Euclidean distance between this coordinate and another.
*
* @param other The other RoomCords instance
* @return The distance as a double
*/

View File

@@ -3,6 +3,10 @@ package cz.jzitnik.utils;
// Don't blame me that I'm using field injection instead of construction injection. I just like it more, leave me alone.
// Yes, I know I'll suffer in the unit tests. (who said there will be any? hmmm)
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.MutableClassToInstanceMap;
import cz.jzitnik.annotations.Config;
@@ -21,7 +25,7 @@ import java.lang.reflect.Method;
import java.util.*;
@Slf4j
public class DependencyManager {
public class DependencyManager extends InjectableValues {
private final ClassToInstanceMap<Object> configs = MutableClassToInstanceMap.create();
private final ClassToInstanceMap<Object> data = MutableClassToInstanceMap.create();
@@ -184,4 +188,39 @@ public class DependencyManager {
}
}
}
@Override
public Object findInjectableValue(Object valueId,
DeserializationContext ctxt,
BeanProperty forProperty,
Object beanInstance) throws JsonMappingException {
if (valueId instanceof Class<?> clazz) {
Object dep = data.getInstance(clazz);
if (dep != null) return dep;
dep = configs.getInstance(clazz);
if (dep != null) return dep;
if (clazz == this.getClass()) return this;
ctxt.reportInputMismatch(forProperty,
"No injectable value found for type: %s", clazz.getName());
} else if (valueId instanceof String key) {
for (Object dep: data.values()) {
if (dep.getClass().getName().equalsIgnoreCase(key)) return dep;
}
for (Object dep : configs.values()) {
if (dep.getClass().getName().equalsIgnoreCase(key)) return dep;
}
ctxt.reportInputMismatch(forProperty,
"No injectable value found for key: %s", key);
} else {
ctxt.reportInputMismatch(forProperty,
"Unrecognized inject value id type (%s), expecting Class or String",
valueId.getClass().getName());
}
return null;
}
}

View File

@@ -1,5 +1,6 @@
package cz.jzitnik.utils.roomtasks;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -8,7 +9,10 @@ import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor
@Getter
public abstract class RoomTask {
@JsonIgnore
private final Runnable task;
@JsonIgnore
private final long rate;
@JsonIgnore
private final TimeUnit rateUnit;
}

View File

@@ -0,0 +1,11 @@
playerCords:
x: 90
y: 100
collider:
start:
x: 0
y: 52
end:
x: 44
y: 78

View File

@@ -0,0 +1,64 @@
- id: "mainroom"
texture: "ROOM1"
objects:
- objectType: "chest"
cords:
x: 100
y: 45
items:
- name: "Wooden sword"
type:
name: "weapon_sword"
dealDamage: 1
texture: "WOODEN_SWORD"
- name: "Apple"
type:
name: "food"
addHealth: 1
texture: "APPLE"
colliders:
- start:
x: 100
y: 45
end:
x: 140
y: 67
mobs:
- type: "hittable_drops"
texture: "PLAYER_FRONT"
cords:
x: 100
y: 100
collider:
start:
x: 0
y: 52
end:
x: 44
y: 78
health: 10
itemsDrops:
- name: "Apple"
type:
name: "food"
addHealth: 1
texture: "APPLE"
tasks:
- type: "blind_following_player"
speed: 1
updateRateMs: 100
- type: "attacking_player"
damage: 5
reach: 15
updateRateMs: 500
west: null
east: "secondroom"
north: null
south: null
- id: "secondroom"
texture: "ROOM2"
west: "mainroom"
east: null
north: null
south: null