60 lines
2.0 KiB
Python
60 lines
2.0 KiB
Python
"""World state CRUD: rules, locations, event log.
|
|
|
|
Stored in narrative/world_state.json. Missing file is treated as an empty
|
|
world so existing simulations (from pre-God-Mode versions) continue to work
|
|
without migration.
|
|
"""
|
|
import os
|
|
import json
|
|
|
|
|
|
class WorldStateStore:
|
|
"""Manages narrative/world_state.json for a single simulation."""
|
|
|
|
def __init__(self, sim_dir: str):
|
|
self.sim_dir = sim_dir
|
|
self.narrative_dir = os.path.join(sim_dir, "narrative")
|
|
self.path = os.path.join(self.narrative_dir, "world_state.json")
|
|
|
|
def _ensure_dir(self) -> None:
|
|
os.makedirs(self.narrative_dir, exist_ok=True)
|
|
|
|
def load(self) -> dict:
|
|
if not os.path.exists(self.path):
|
|
return {"rules": [], "locations": {}, "event_log": []}
|
|
with open(self.path, "r", encoding="utf-8") as f:
|
|
world = json.load(f)
|
|
# Fill in missing keys for forward compatibility
|
|
world.setdefault("rules", [])
|
|
world.setdefault("locations", {})
|
|
world.setdefault("event_log", [])
|
|
return world
|
|
|
|
def save(self, world: dict) -> None:
|
|
self._ensure_dir()
|
|
with open(self.path, "w", encoding="utf-8") as f:
|
|
json.dump(world, f, ensure_ascii=False, indent=2)
|
|
|
|
def set_rules(self, rules: list[str]) -> None:
|
|
world = self.load()
|
|
world["rules"] = list(rules)
|
|
self.save(world)
|
|
|
|
def upsert_location(self, location: dict) -> dict:
|
|
"""Insert or update a location by id. Returns the stored entry."""
|
|
if "id" not in location:
|
|
raise ValueError("location requires 'id'")
|
|
world = self.load()
|
|
world["locations"][location["id"]] = location
|
|
self.save(world)
|
|
return location
|
|
|
|
def append_event(self, event: dict) -> dict:
|
|
"""Append an event to event_log, assigning evt_N id automatically."""
|
|
world = self.load()
|
|
event = dict(event)
|
|
event["id"] = f"evt_{len(world['event_log']) + 1}"
|
|
world["event_log"].append(event)
|
|
self.save(world)
|
|
return event
|