import sqlite3 from pathlib import Path import pytest from engine.character import Ailment, BodyPart, Character, ClothingPiece from engine.db import WorldRepository from engine.defs import DefRegistry from engine.entity import EntityPosition from engine.inventory import Inventory from engine.item import ItemInstance from engine.map_loader import MapLoader from engine.world import World DEFS_DIR = Path(__file__).resolve().parent.parent / "resources" / "defs" @pytest.fixture() def registry(): return DefRegistry.load(DEFS_DIR) def build_world(registry) -> World: loader = MapLoader(DEFS_DIR / "maps") demo_world = loader.load("demo_world.json") ship = demo_world.embeddings[0].child_map world = World(registry=registry) world.add_map(demo_world) world.add_map(ship) player = Character( entity_id="player-1", name="Player", def_id="player", position=EntityPosition("demo_world", 7, 5, 0), species_id="human", gender="nonbinary", blood_percentage=82.5, body_parts=[ BodyPart("head", "human_head_hat"), BodyPart("left_leg", "human_left_leg", integrity=40.0, ailments=[Ailment("frostbite", "Frostbite", 0.5)]), ], default_body_parts=registry.new_default_body_parts("human"), clothing=[ClothingPiece("torso", "leather_jacket")], default_clothing=registry.new_default_clothing("player"), mental_stats={"insanity": 12.5}, ) player.bio["stealth"] = 3 backpack_inventory = player.get_equipped_inventory("back", registry) backpack_inventory.place(ItemInstance("rope-1", "sandwich"), registry.get_item_def("sandwich"), 0, 0) player.inventory = Inventory(6, 5) player.inventory.place(ItemInstance("staff-1", "staff_oak"), registry.get_item_def("staff_oak"), 0, 0) player.inventory.place(ItemInstance("sandwich-1", "sandwich"), registry.get_item_def("sandwich"), 1, 0) world.add_entity(player) world.player = player return world def test_save_and_load_roundtrip_preserves_entity_and_inventory(registry): world = build_world(registry) repo = WorldRepository(sqlite3.connect(":memory:")) repo.save_world(world) loader = MapLoader(DEFS_DIR / "maps") reloaded = repo.load_world(registry, loader) assert reloaded is not None assert reloaded.player is not None assert reloaded.player.entity_id == "player-1" assert (reloaded.player.position.map_id, reloaded.player.position.x, reloaded.player.position.y) == ( "demo_world", 7, 5, ) assert isinstance(reloaded.player, Character) assert reloaded.player.get_body_part("head").part_def_id == "human_head_hat" assert reloaded.player.get_body_part("torso").part_def_id == "human_torso" placed = {p.item.item_id: (p.x, p.y) for p in reloaded.player.inventory.items()} assert placed == {"staff-1": (0, 0), "sandwich-1": (1, 0)} def test_save_and_load_roundtrip_preserves_health_fields(registry): world = build_world(registry) repo = WorldRepository(sqlite3.connect(":memory:")) repo.save_world(world) loader = MapLoader(DEFS_DIR / "maps") reloaded = repo.load_world(registry, loader) player = reloaded.player assert player.species_id == "human" assert player.gender == "nonbinary" assert player.blood_percentage == pytest.approx(82.5) assert player.mental_stats == {"insanity": 12.5} assert player.bio["stealth"] == 3 assert player.bio["athletics"] == 0 left_leg = player.get_body_part("left_leg") assert left_leg.integrity == pytest.approx(40.0) assert [a.id for a in left_leg.ailments] == ["frostbite"] assert left_leg.ailments[0].severity == pytest.approx(0.5) assert player.get_clothing("torso").item_def_id == "leather_jacket" backpack = player.get_clothing("back") assert backpack.item_def_id == "backpack_basic" assert backpack.inventory is not None assert (backpack.inventory.width, backpack.inventory.height) == (4, 4) assert {p.item.item_id for p in backpack.inventory.items()} == {"rope-1"} def test_load_world_returns_none_when_no_save_exists(registry): repo = WorldRepository(sqlite3.connect(":memory:")) loader = MapLoader(DEFS_DIR / "maps") assert repo.load_world(registry, loader) is None def test_runtime_tile_edits_survive_save_and_load(registry): world = build_world(registry) demo_world = world.maps["demo_world"] # simulate base-building: knock a wall down on the ship, damage a flooring tile elsewhere ship = world.maps["ship_small"] ship.get_tile(0, 0, 0).room = None demo_world.get_tile(3, 3, 0).flooring.integrity = 42.0 repo = WorldRepository(sqlite3.connect(":memory:")) repo.save_world(world) loader = MapLoader(DEFS_DIR / "maps") reloaded = repo.load_world(registry, loader) reloaded_ship = reloaded.maps["ship_small"] assert reloaded_ship.get_tile(0, 0, 0).room is None reloaded_demo = reloaded.maps["demo_world"] edited_tile = reloaded_demo.get_tile(3, 3, 0) assert edited_tile.flooring.def_id == "grass" assert edited_tile.flooring.integrity == pytest.approx(42.0)