diff --git a/src/main/java/neon/entities/Door.java b/src/main/java/neon/entities/Door.java
index d3e1c31..5b3140b 100644
--- a/src/main/java/neon/entities/Door.java
+++ b/src/main/java/neon/entities/Door.java
@@ -18,7 +18,8 @@
package neon.entities;
-import lombok.Setter;
+import java.util.Objects;
+import lombok.Getter;
import neon.entities.components.DoorRenderComponent;
import neon.entities.components.Lock;
import neon.entities.components.Portal;
@@ -30,8 +31,7 @@ public class Door extends Item {
public final Lock lock;
public final Trap trap;
public final Portal portal;
-
- @Setter private String sign;
+ @Getter private String sign = "";
public Door(long uid, RItem resource) {
super(uid, resource);
@@ -42,11 +42,15 @@ public Door(long uid, RItem resource) {
}
public boolean hasSign() {
- return sign != null;
+ return sign != null && !sign.isEmpty();
+ }
+
+ public void setSign(String newSign) {
+ sign = Objects.requireNonNullElse(newSign, "");
}
public String toString() {
- if (sign != null) {
+ if (sign != null && !sign.isEmpty()) {
return sign;
} else {
return super.toString();
diff --git a/src/main/java/neon/entities/serialization/EntitySerializer.java b/src/main/java/neon/entities/serialization/EntitySerializer.java
deleted file mode 100644
index 34a13c3..0000000
--- a/src/main/java/neon/entities/serialization/EntitySerializer.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Neon, a roguelike engine.
- * Copyright (C) 2012 - Maarten Driesen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package neon.entities.serialization;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import neon.core.GameContext;
-import neon.entities.Creature;
-import neon.entities.Entity;
-import neon.entities.Item;
-
-public class EntitySerializer {
- private static final long serialVersionUID = 4682346337753485512L;
- private final GameContext gameContext;
- private final ItemSerializer itemSerializer;
- private final CreatureSerializer creatureSerializer;
-
- public EntitySerializer(GameContext gameContext) {
- this.gameContext = gameContext;
- itemSerializer = new ItemSerializer(gameContext);
- creatureSerializer = new CreatureSerializer(gameContext);
- }
-
- public Entity deserialize(DataInput input) throws IOException {
- switch (input.readUTF()) {
- case "item":
- return itemSerializer.deserialize(input);
- case "creature":
- return creatureSerializer.deserialize(input);
- default:
- return null;
- }
- }
-
- public void serialize(DataOutput output, Entity entity) throws IOException {
- if (entity instanceof Item) {
- output.writeUTF("item");
- itemSerializer.serialize(output, (Item) entity);
- } else if (entity instanceof Creature) {
- output.writeUTF("creature");
- creatureSerializer.serialize(output, (Creature) entity);
- }
- }
-}
diff --git a/src/main/java/neon/entities/serialization/ItemSerializer.java b/src/main/java/neon/entities/serialization/ItemSerializer.java
index 154fd5d..f5256bc 100644
--- a/src/main/java/neon/entities/serialization/ItemSerializer.java
+++ b/src/main/java/neon/entities/serialization/ItemSerializer.java
@@ -104,7 +104,7 @@ public void serialize(DataOutput output, Item item) throws IOException {
switch (item) {
case Door door -> {
- output.writeUTF(door.toString());
+ output.writeUTF(door.getSign());
writePortal(output, door.portal);
writeLock(output, door.lock);
writeTrap(output, door.trap);
diff --git a/src/test/java/neon/entities/serialization/EntitySerializationTest.java b/src/test/java/neon/entities/serialization/EntitySerializationTest.java
new file mode 100644
index 0000000..109bc24
--- /dev/null
+++ b/src/test/java/neon/entities/serialization/EntitySerializationTest.java
@@ -0,0 +1,615 @@
+package neon.entities.serialization;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.stream.Stream;
+import neon.core.DefaultUIEngineContext;
+import neon.editor.DataStore;
+import neon.entities.*;
+import neon.entities.property.Slot;
+import neon.resources.*;
+import neon.systems.files.FileSystem;
+import neon.test.MapDbTestHelper;
+import neon.test.TestEngineContext;
+import neon.util.mapstorage.MapStore;
+import org.h2.mvstore.WriteBuffer;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * Comprehensive test for entity serialization and deserialization. Tests all resources from
+ * sampleMod1 (88+ items, 136+ creatures) by: 1. Creating entity instances from resources 2.
+ * Serializing using EntitySerializerFactory 3. Deserializing and verifying complete fidelity
+ */
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class EntitySerializationTest {
+
+ // Test infrastructure
+ private MapStore testDb;
+ private DefaultUIEngineContext testContext;
+ private EntitySerializerFactory serializerFactory;
+ private EntityFactory entityFactory;
+ private DataStore dataStore;
+
+ // Resource collections
+ private Map itemResources;
+ private Map creatureResources;
+
+ @BeforeAll
+ public void setUp() throws Exception {
+ // Initialize MapStore database
+ testDb = MapDbTestHelper.createTempFileDb();
+
+ // Initialize TestEngineContext
+ TestEngineContext.initialize(testDb);
+ testContext = TestEngineContext.getTestUiEngineContext();
+
+ // Mount file system and load resources
+ FileSystem fileSystem = testContext.getFileSystem();
+ fileSystem.mount("src/test/resources/");
+
+ dataStore = new DataStore(testContext.getResourceManageer(), fileSystem);
+ dataStore.loadData("sampleMod1", true, false);
+
+ // Create factories
+ serializerFactory = new EntitySerializerFactory(testContext);
+ entityFactory = new EntityFactory(testContext);
+
+ // Build resource maps
+ itemResources = buildItemResourceMap(testContext.getResourceManageer());
+ creatureResources = buildCreatureResourceMap(testContext.getResourceManageer());
+
+ System.out.println("Loaded " + itemResources.size() + " item resources");
+ System.out.println("Loaded " + creatureResources.size() + " creature resources");
+ }
+
+ @AfterAll
+ public void tearDown() {
+ TestEngineContext.reset();
+ MapDbTestHelper.cleanup(testDb);
+ }
+
+ // ==================== TIER 1: PARAMETERIZED BULK TESTS ====================
+
+ @ParameterizedTest(name = "[{index}] {0}")
+ @MethodSource("provideAllItemResources")
+ public void testItemSerializationRoundTrip(ItemTestCase testCase) {
+ // Create entity
+ long uid = testContext.getStore().createNewEntityUID();
+ Item original = entityFactory.getItem(testCase.resourceId, 10, 20, uid);
+ assertNotNull(original, "Failed to create item: " + testCase.resourceId);
+
+ // Serialize and deserialize
+ Item deserialized = serializeAndDeserialize(original);
+
+ // Verify
+ assertItemEquals(original, deserialized, testCase.itemType);
+ }
+
+ @ParameterizedTest(name = "[{index}] {0}")
+ @MethodSource("provideAllCreatureResources")
+ public void testCreatureSerializationRoundTrip(CreatureTestCase testCase) {
+ // Create entity
+ long uid = testContext.getStore().createNewEntityUID();
+ Creature original = entityFactory.getCreature(testCase.resourceId, 10, 20, uid);
+ assertNotNull(original, "Failed to create creature: " + testCase.resourceId);
+
+ // Serialize and deserialize
+ Creature deserialized = serializeAndDeserialize(original);
+
+ // Verify
+ assertCreatureEquals(original, deserialized, testCase.type);
+ }
+
+ // ==================== TIER 2: TYPE-SPECIFIC TESTS ====================
+
+ @Test
+ public void testWeaponSerialization() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Weapon weapon = (Weapon) entityFactory.getItem("dagger", 5, 10, uid);
+ weapon.setState(50); // Set durability
+
+ Weapon deserialized = serializeAndDeserialize(weapon);
+
+ assertItemEquals(weapon, deserialized, ItemType.WEAPON);
+ assertWeaponEquals(weapon, deserialized);
+ }
+
+ @Test
+ public void testArmorSerialization() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Armor armor = (Armor) entityFactory.getItem("leather chausses", 5, 10, uid);
+ armor.setState(75); // Set durability
+
+ Armor deserialized = serializeAndDeserialize(armor);
+
+ assertItemEquals(armor, deserialized, ItemType.ARMOR);
+ assertArmorEquals(armor, deserialized);
+ }
+
+ @Test
+ public void testDoorSerialization() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Door door = (Door) entityFactory.getItem("door", 5, 10, uid);
+
+ // Set rich state
+ door.setSign("Tavern");
+ door.lock.setLockDC(15);
+ door.lock.lock();
+ door.trap.setTrapDC(20);
+ door.portal.setDestMap(123);
+ door.portal.setDestZone(1);
+
+ Door deserialized = serializeAndDeserialize(door);
+
+ assertItemEquals(door, deserialized, ItemType.DOOR);
+ assertDoorEquals(door, deserialized);
+ }
+
+ @Test
+ public void testContainerSerialization() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Container container = (Container) entityFactory.getItem("chest", 5, 10, uid);
+
+ // Add items to container
+ long item1Uid = testContext.getStore().createNewEntityUID();
+ long item2Uid = testContext.getStore().createNewEntityUID();
+ container.addItem(item1Uid);
+ container.addItem(item2Uid);
+
+ // Set lock and trap
+ container.lock.setLockDC(10);
+ container.lock.unlock();
+ container.trap.setTrapDC(15);
+
+ Container deserialized = serializeAndDeserialize(container);
+
+ assertItemEquals(container, deserialized, ItemType.CONTAINER);
+ assertContainerEquals(container, deserialized);
+ }
+
+ @Test
+ public void testHominidSerialization() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Creature hominid = entityFactory.getCreature("tengri2", 5, 10, uid);
+
+ assertTrue(hominid instanceof Hominid, "Expected Hominid creature");
+
+ // Set health and money
+ hominid.getHealthComponent().heal(10);
+ hominid.getInventoryComponent().addMoney(100);
+
+ Creature deserialized = serializeAndDeserialize(hominid);
+
+ assertCreatureEquals(hominid, deserialized, CreatureType.HOMINID);
+ assertTrue(deserialized instanceof Hominid);
+ }
+
+ @Test
+ public void testConstructSerialization() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Creature construct = entityFactory.getCreature("gargoyle", 5, 10, uid);
+
+ assertTrue(construct instanceof Construct, "Expected Construct creature");
+
+ Creature deserialized = serializeAndDeserialize(construct);
+
+ assertCreatureEquals(construct, deserialized, CreatureType.CONSTRUCT);
+ assertTrue(deserialized instanceof Construct);
+ }
+
+ @Test
+ public void testDragonSerialization() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Creature dragon = entityFactory.getCreature("red dragon", 5, 10, uid);
+
+ assertTrue(dragon instanceof Dragon, "Expected Dragon creature");
+
+ Creature deserialized = serializeAndDeserialize(dragon);
+
+ assertCreatureEquals(dragon, deserialized, CreatureType.DRAGON);
+ assertTrue(deserialized instanceof Dragon);
+ }
+
+ @Test
+ public void testDaemonSerialization() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Creature daemon = entityFactory.getCreature("frost daemon", 5, 10, uid);
+
+ assertTrue(daemon instanceof Daemon, "Expected Daemon creature");
+
+ Creature deserialized = serializeAndDeserialize(daemon);
+
+ assertCreatureEquals(daemon, deserialized, CreatureType.DAEMON);
+ assertTrue(deserialized instanceof Daemon);
+ }
+
+ // ==================== TIER 3: EDGE CASES ====================
+
+ @Test
+ public void testCreatureWithInventory() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Creature creature = entityFactory.getCreature("tengri2", 5, 10, uid);
+
+ // Add items to inventory
+ long item1Uid = testContext.getStore().createNewEntityUID();
+ long item2Uid = testContext.getStore().createNewEntityUID();
+ Item weapon = entityFactory.getItem("dagger", 0, 0, item1Uid);
+ Item armor = entityFactory.getItem("leather chausses", 0, 0, item2Uid);
+
+ creature.getInventoryComponent().addItem(weapon.getUID());
+ creature.getInventoryComponent().addItem(armor.getUID());
+ creature.getInventoryComponent().put(Slot.WEAPON, weapon.getUID());
+ creature.getInventoryComponent().put(Slot.CUIRASS, armor.getUID());
+ creature.getInventoryComponent().addMoney(50);
+
+ Creature deserialized = serializeAndDeserialize(creature);
+
+ assertCreatureEquals(creature, deserialized, CreatureType.HOMINID);
+ assertEquals(
+ creature.getInventoryComponent().getItems().size(),
+ deserialized.getInventoryComponent().getItems().size());
+ assertEquals(
+ creature.getInventoryComponent().get(Slot.WEAPON),
+ deserialized.getInventoryComponent().get(Slot.WEAPON));
+ assertEquals(
+ creature.getInventoryComponent().get(Slot.CUIRASS),
+ deserialized.getInventoryComponent().get(Slot.CUIRASS));
+ }
+
+ @Test
+ public void testCreatureWithScripts() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Creature creature = entityFactory.getCreature("tengri2", 5, 10, uid);
+
+ // Add scripts
+ creature.getScriptComponent().addScript("death_script.js");
+ creature.getScriptComponent().addScript("attack_script.js");
+
+ Creature deserialized = serializeAndDeserialize(creature);
+
+ assertCreatureEquals(creature, deserialized, CreatureType.HOMINID);
+ assertEquals(
+ creature.getScriptComponent().getScripts(), deserialized.getScriptComponent().getScripts());
+ }
+
+ @Test
+ public void testItemWithOwner() {
+ long ownerUid = testContext.getStore().createNewEntityUID();
+ long itemUid = testContext.getStore().createNewEntityUID();
+ Item item = entityFactory.getItem("dagger", 5, 10, itemUid);
+
+ item.setOwner(ownerUid);
+
+ Item deserialized = serializeAndDeserialize(item);
+
+ assertItemEquals(item, deserialized, ItemType.WEAPON);
+ assertEquals(ownerUid, deserialized.getOwner());
+ }
+
+ @Test
+ public void testEmptyContainer() {
+ long uid = testContext.getStore().createNewEntityUID();
+ Container container = (Container) entityFactory.getItem("chest", 5, 10, uid);
+
+ // Don't add any items
+ assertTrue(container.getItems().isEmpty());
+
+ Container deserialized = serializeAndDeserialize(container);
+
+ assertItemEquals(container, deserialized, ItemType.CONTAINER);
+ assertTrue(deserialized.getItems().isEmpty());
+ }
+
+ @Test
+ public void testBulkSerializationPerformance() {
+ long startTime = System.currentTimeMillis();
+
+ // Serialize 200 entities
+ for (int i = 0; i < 200; i++) {
+ long uid = testContext.getStore().createNewEntityUID();
+ if (i % 2 == 0) {
+ // Create item
+ String resourceId =
+ itemResources.keySet().stream()
+ .skip(i % itemResources.size())
+ .findFirst()
+ .orElse("dagger");
+ Item item = entityFactory.getItem(resourceId, 0, 0, uid);
+ serializeAndDeserialize(item);
+ } else {
+ // Create creature
+ String resourceId =
+ creatureResources.keySet().stream()
+ .skip(i % creatureResources.size())
+ .findFirst()
+ .orElse("bandit");
+ Creature creature = entityFactory.getCreature(resourceId, 0, 0, uid);
+ serializeAndDeserialize(creature);
+ }
+ }
+
+ long duration = System.currentTimeMillis() - startTime;
+ System.out.println("Bulk serialization of 200 entities took: " + duration + "ms");
+
+ assertTrue(duration < 2000, "Bulk serialization took too long: " + duration + "ms");
+ }
+
+ @Test
+ public void testResourceCoverage() {
+ // Verify we have the expected number of resources
+ assertTrue(
+ itemResources.size() >= 80,
+ "Expected at least 80 item resources, got " + itemResources.size());
+ assertTrue(
+ creatureResources.size() >= 120,
+ "Expected at least 120 creature resources, got " + creatureResources.size());
+ }
+
+ // ==================== HELPER METHODS ====================
+
+ private T serializeAndDeserialize(T original) {
+ WriteBuffer writeBuffer = new WriteBuffer();
+ serializerFactory.writeEntityToWriteBuffer(writeBuffer, original);
+
+ byte[] serialized = writeBuffer.getBuffer().array();
+ ByteBuffer readBuffer = ByteBuffer.wrap(serialized);
+
+ @SuppressWarnings("unchecked")
+ T result = (T) serializerFactory.readEntityFromByteBuffer(readBuffer);
+ return result;
+ }
+
+ private Map buildItemResourceMap(ResourceManager rm) {
+ return rm.getResources(RItem.class).stream()
+ .filter(r -> !(r instanceof LItem))
+ .collect(java.util.stream.Collectors.toMap(r -> r.id, r -> r));
+ }
+
+ private Map buildCreatureResourceMap(ResourceManager rm) {
+ return rm.getResources(RCreature.class).stream()
+ .filter(r -> !(r instanceof LCreature))
+ .collect(java.util.stream.Collectors.toMap(r -> r.id, r -> r));
+ }
+
+ private ItemType determineItemType(RItem resource) {
+ RItem.Type type = resource.type;
+ return switch (type) {
+ case weapon -> ItemType.WEAPON;
+ case armor -> ItemType.ARMOR;
+ case clothing -> ItemType.CLOTHING;
+ case door -> ItemType.DOOR;
+ case container -> ItemType.CONTAINER;
+ case food -> ItemType.FOOD;
+ case aid -> ItemType.AID;
+ case light -> ItemType.LIGHT;
+ case potion -> ItemType.POTION;
+ case scroll -> ItemType.SCROLL;
+ case book -> ItemType.BOOK;
+ case coin -> ItemType.COIN;
+ default -> ItemType.GENERIC;
+ };
+ }
+
+ private CreatureType determineCreatureType(RCreature resource) {
+ RCreature.Type type = resource.type;
+ return switch (type) {
+ case humanoid -> CreatureType.HOMINID;
+ case construct -> CreatureType.CONSTRUCT;
+ case dragon -> CreatureType.DRAGON;
+ case daemon -> CreatureType.DAEMON;
+ default -> CreatureType.GENERIC;
+ };
+ }
+
+ // ==================== VERIFICATION METHODS ====================
+
+ private void assertEntityBaseEquals(Entity expected, Entity actual) {
+ assertNotSame(expected, actual, "Should be different instances");
+ assertEquals(expected.getUID(), actual.getUID(), "UID should match");
+ assertEquals(
+ expected.getShapeComponent().getX(),
+ actual.getShapeComponent().getX(),
+ "X coordinate should match");
+ assertEquals(
+ expected.getShapeComponent().getY(),
+ actual.getShapeComponent().getY(),
+ "Y coordinate should match");
+ }
+
+ private void assertItemEquals(Item expected, Item actual, ItemType type) {
+ assertEntityBaseEquals(expected, actual);
+ assertEquals(expected.getOwner(), actual.getOwner(), "Owner should match");
+ assertEquals(expected.resource.id, actual.resource.id, "Resource ID should match");
+
+ // Type-specific checks
+ switch (type) {
+ case DOOR -> {
+ if (expected instanceof Door expectedDoor && actual instanceof Door actualDoor) {
+ assertDoorEquals(expectedDoor, actualDoor);
+ }
+ }
+ case CONTAINER -> {
+ if (expected instanceof Container expectedContainer
+ && actual instanceof Container actualContainer) {
+ assertContainerEquals(expectedContainer, actualContainer);
+ }
+ }
+ case ARMOR -> {
+ if (expected instanceof Armor expectedArmor && actual instanceof Armor actualArmor) {
+ assertArmorEquals(expectedArmor, actualArmor);
+ }
+ }
+ case WEAPON -> {
+ if (expected instanceof Weapon expectedWeapon && actual instanceof Weapon actualWeapon) {
+ assertWeaponEquals(expectedWeapon, actualWeapon);
+ }
+ }
+ }
+ }
+
+ private void assertDoorEquals(Door expected, Door actual) {
+ assertEquals(expected.hasSign(), actual.hasSign(), "Door sign presence should match");
+ if (expected.hasSign()) {
+ assertEquals(expected.toString(), actual.toString(), "Door sign should match");
+ }
+ assertEquals(expected.lock.isLocked(), actual.lock.isLocked(), "Lock state should match");
+ assertEquals(expected.lock.getLockDC(), actual.lock.getLockDC(), "Lock DC should match");
+ assertEquals(expected.trap.getTrapDC(), actual.trap.getTrapDC(), "Trap DC should match");
+ assertEquals(
+ expected.portal.getDestMap(),
+ actual.portal.getDestMap(),
+ "Portal destination map should match");
+ assertEquals(
+ expected.portal.getDestZone(),
+ actual.portal.getDestZone(),
+ "Portal destination zone should match");
+ }
+
+ private void assertContainerEquals(Container expected, Container actual) {
+ assertEquals(
+ expected.getItems().size(), actual.getItems().size(), "Container items count should match");
+ assertEquals(expected.lock.isLocked(), actual.lock.isLocked(), "Lock state should match");
+ assertEquals(expected.lock.getLockDC(), actual.lock.getLockDC(), "Lock DC should match");
+ assertEquals(expected.trap.getTrapDC(), actual.trap.getTrapDC(), "Trap DC should match");
+
+ // Verify all item UIDs match
+ for (int i = 0; i < expected.getItems().size(); i++) {
+ assertEquals(
+ expected.getItems().get(i),
+ actual.getItems().get(i),
+ "Container item UID at index " + i + " should match");
+ }
+ }
+
+ private void assertArmorEquals(Armor expected, Armor actual) {
+ assertEquals(expected.getState(), actual.getState(), "Armor state should match");
+ }
+
+ private void assertWeaponEquals(Weapon expected, Weapon actual) {
+ assertEquals(expected.getState(), actual.getState(), "Weapon state should match");
+ }
+
+ private void assertCreatureEquals(Creature expected, Creature actual, CreatureType type) {
+ assertEntityBaseEquals(expected, actual);
+ assertEquals(expected.getName(), actual.getName(), "Name should match");
+ assertEquals(expected.species.id, actual.species.id, "Species ID should match");
+
+ // Health component
+ assertEquals(
+ expected.getHealthComponent().getBaseHealth(),
+ actual.getHealthComponent().getBaseHealth(),
+ "Base health should match");
+ assertEquals(
+ expected.getHealthComponent().getHealth(),
+ actual.getHealthComponent().getHealth(),
+ "Current health should match");
+
+ // Inventory
+ assertEquals(
+ expected.getInventoryComponent().getMoney(),
+ actual.getInventoryComponent().getMoney(),
+ "Money should match");
+ assertEquals(
+ expected.getInventoryComponent().getItems().size(),
+ actual.getInventoryComponent().getItems().size(),
+ "Inventory size should match");
+
+ // Equipment slots
+ for (Slot slot : Slot.values()) {
+ assertEquals(
+ expected.getInventoryComponent().get(slot),
+ actual.getInventoryComponent().get(slot),
+ "Equipment slot " + slot + " should match");
+ }
+
+ // Scripts
+ assertEquals(
+ expected.getScriptComponent().getScripts(),
+ actual.getScriptComponent().getScripts(),
+ "Scripts should match");
+
+ // Type verification
+ Class> expectedClass = getExpectedCreatureClass(type);
+ assertTrue(
+ expectedClass.isInstance(actual),
+ "Creature should be of type " + expectedClass.getSimpleName());
+ }
+
+ private Class> getExpectedCreatureClass(CreatureType type) {
+ return switch (type) {
+ case HOMINID -> Hominid.class;
+ case CONSTRUCT -> Construct.class;
+ case DRAGON -> Dragon.class;
+ case DAEMON -> Daemon.class;
+ case GENERIC -> Creature.class;
+ };
+ }
+
+ // ==================== METHOD SOURCES ====================
+
+ private Stream provideAllItemResources() throws Exception {
+ // This will be populated during test execution
+
+ return itemResources.entrySet().stream()
+ .map(
+ entry ->
+ new ItemTestCase(
+ entry.getKey(), entry.getValue(), determineItemType(entry.getValue())));
+ }
+
+ private Stream provideAllCreatureResources() throws Exception {
+ // This will be populated during test execution
+
+ return creatureResources.entrySet().stream()
+ .map(
+ entry ->
+ new CreatureTestCase(
+ entry.getKey(), entry.getValue(), determineCreatureType(entry.getValue())));
+ }
+
+ // ==================== TEST CASE RECORDS ====================
+
+ record ItemTestCase(String resourceId, RItem resource, ItemType itemType) {
+ @Override
+ public String toString() {
+ return resourceId + " (" + itemType + ")";
+ }
+ }
+
+ record CreatureTestCase(String resourceId, RCreature resource, CreatureType type) {
+ @Override
+ public String toString() {
+ return resourceId + " (" + type + ")";
+ }
+ }
+
+ // ==================== ENUMS ====================
+
+ enum ItemType {
+ WEAPON,
+ ARMOR,
+ CLOTHING,
+ DOOR,
+ CONTAINER,
+ FOOD,
+ AID,
+ LIGHT,
+ POTION,
+ SCROLL,
+ BOOK,
+ COIN,
+ GENERIC
+ }
+
+ enum CreatureType {
+ HOMINID,
+ CONSTRUCT,
+ DRAGON,
+ DAEMON,
+ GENERIC
+ }
+}