Data files and the rate grammar¶
designed · Phase 1 (#29, #32) · PLAN §5.5, §6.10, §15.2, §18.5, §22.2
data/
├── templates/ npc_villager.template.json, building_wooden, berry_bush, wood_pile, campfire, bucket
├── scenarios/ village.scenario.json
├── materials/ (Phase 1: unused)
├── rules/ (Phase 3)
└── world-schema.json
Templates¶
{ id, kind, components[], attributes{} }. An attribute value is a number, a string, a bool, { "value", "variance" } or { "min", "max" } (sampled at spawn from the seeded RNG), or a rate string.
The rate grammar¶
Rates and durations are stored in sim-time with the unit in the key. A data file may write the value as a number in that unit or as a string the loader parses:
<number> [± <number>] <unit> "3 hours" "60 ± 15 years" "2 days"
<number> / <unit> "100/day" "80/day" "0.4/hour"
units: minutes | hours | days | weeks | seasons | years
The parsed value must match the unit the key names: "hunger_rate_per_day": "100/day" is fine; "hunger_rate_per_day": "4/hour" is converted (96/day); "hunger_rate_per_day": "3 hours" is rejected (a duration is not a rate). A bare per-tick number for a rate key, or any key ending in _per_tick, is rejected at load — invariant 9.
Keys: *_per_day, *_per_hour, *_hours, *_days, *_years, *_minutes.
Scenario¶
{ "id": "village", "world": { "ticks_per_day": 200, "ambient_temperature": 15 },
"locations": [ { "id": "inn", "template": "building_wooden", "components": ["ThermalBody"] }, … ],
"edges": [ { "from": "fire_pit", "to": "inn", "relation": "connected_to",
"properties": { "permeability.heat": 0.4, "permeability.sound": 0.5, "permeability.light": 0.6 } }, … ],
"factions": [ { "id": "villagers", "knows": { "locations": "all", "resources": "all" } } ],
"spawn": [ { "template": "npc_villager", "count": 20, "in": "village_square", "faction": "villagers" }, … ] }
factions[].knows seeds knows edges (provenance: "seeded") from the faction node to the named locations and resource nodes (PLAN §7.3, §15.2).
World schema¶
{ "attributes": [ { "name": "food_value", "type": "float", "bounds": { "min": 0 }, "depletable": true, "salient": true }, … ],
"components": [ … ], "events": [ … ] }
A depletable attribute without min is rejected at load. salient marks what PerceptionSystem records as facts. Manifests add their contracts at load to form the live schema (reads/writes for components, attributes, edges, events; writes.actions with needs).