feat(newgame): the seeded creation pipeline

construct() takes a seed, not a stat block. It rebuilds the RNG and rolls the
attributes itself, so it never trusts a number handed to it (§2) and the same seed
yields an identical character, hidden Luck included (§10). The whole character is
reproducible from four primitives: seed, race_id, calling_id, spend. The rng
parameter is dropped — the seed is now the only source of randomness.

The roll is 3d6 with a hard floor of 8. Straight 3d6 puts ~9% on a primary the +3
pool cannot rescue, and a character bad at the one thing he is FOR is not grit —
it is the fine §7 forbids, paid for twenty hours.

LCK is unspendable, not merely hidden: spending on it is a validation error.
GameState.stats becomes GameState.sheet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QYa9u7Kdxv5gX4AnwWexy8
This commit is contained in:
2026-07-12 20:43:51 -05:00
parent eba706b906
commit bd49e118d5
5 changed files with 298 additions and 70 deletions

View File

@@ -8,10 +8,17 @@ const CanonLog = preload("res://scripts/canon_log/canon_log.gd")
func _built_log():
var world = ContentDB.new()
world.load_from(ContentDB.default_content_root())
var rng := RandomNumberGenerator.new()
rng.seed = 3
var origin: Dictionary = ContentDB.load_json(ContentDB.origin_path("deserter"))
var res := NewGame.construct(origin, world, {"name": "Aldric", "race_id": "human", "calling_id": "bonesetter"}, rng)
var creation := {
"name": "Aldric",
"race_id": "human",
"calling_id": "bonesetter",
"seed": 3,
"spend": {},
"skills": ["faith_lore", "perception"],
"bonus_skill": "endurance",
}
var res := NewGame.construct(origin, world, creation)
assert_true(res["ok"], str(res["errors"]))
return res["log"]