Hi — really enjoying the World Kit system, and I think I've found an inconsistency between it and the character-creation path.
lib/world_kit.py lets a campaign define its own stat_schema (attributes + vitals), and the runtime respects that in most places. But features/character-creation/save_character.py is hardcoded to D&D 5e, so a kit that defines its own stat model can't save a character through /create-character. And it doesn't just reject the sheet — given one, it silently rewrites it.
The specifics
| Line |
Behaviour |
save_character.py:86 |
required_fields = ['name', 'race', 'class', 'level', 'stats'] — a kit without races/classes/levels has nothing to put here |
:105-106 |
hp is recomputed via calculate_hp(class, level, con_modifier) — a hit-die lookup keyed on class name, defaulting to d8. It overwrites whatever HP the kit's own rules produced |
:110 |
saves is derived from D&D class proficiencies — a concept many kits don't have |
:121 |
"gold": character_data.get('gold', 0) is injected unconditionally |
The gold one is the sharpest example of the mismatch: a kit can legitimately have no currency at all (an honour economy, barter, tripods and cattle), and this adds a gold field to every sheet regardless.
Repro
- Create/activate a campaign whose
ruleset.json declares its own vitals, e.g.
"stat_schema": { "attributes": ["bia","podes","thymos","metis","noos","mythos","aidos"], "vitals": ["hp","menos","honour","hubris","miasma","kleos"] }
- Build a character for it and try to save:
bash tools/gm-player.sh save-json "$(cat hero.json)"
→ { "error": "Missing required field: race" }
- Satisfy the required fields with placeholders to get past the gate, and the saved sheet comes back with a d8-derived HP, D&D saving throws, and a
gold field.
Net effect: a non-5e PC has to be written to the campaign's character.json directly, bypassing the supported path entirely.
Suggested fix
Make it kit-aware the same way the rest of the runtime can be — read stat_schema.vitals from the active campaign's ruleset.json:
- require only
name when the kit declares its own vitals; keep the current 5e requirement when it doesn't (so existing D&D campaigns are unchanged);
- run
calculate_hp / calculate_saves only for 5e kits, and otherwise persist the sheet's own vitals untouched;
- don't inject
gold unless the kit actually has it.
I've done the equivalent for player_manager.show_player / show_all_players (which printed Level 1 ... Gold: 0 for every kit) and schemas.validate_character (which required race/class/level of every character) in my fork, gating on stat_schema.vitals and leaving the legacy path byte-for-byte identical when a kit declares none. Happy to open a PR here for save_character.py along the same lines if that'd be useful — just didn't want to send one unsolicited.
Related design question
The /create-character command itself is 5e-shaped end to end (races, classes, spell slots, gold), so fixing the save path alone still leaves the interview assuming D&D. Might be worth deciding whether the command should branch on the active kit, or whether a World Kit should be able to supply its own creation flow.
Environment: Windows 11, Python 3.11, main as of today.
Hi — really enjoying the World Kit system, and I think I've found an inconsistency between it and the character-creation path.
lib/world_kit.pylets a campaign define its ownstat_schema(attributes + vitals), and the runtime respects that in most places. Butfeatures/character-creation/save_character.pyis hardcoded to D&D 5e, so a kit that defines its own stat model can't save a character through/create-character. And it doesn't just reject the sheet — given one, it silently rewrites it.The specifics
save_character.py:86required_fields = ['name', 'race', 'class', 'level', 'stats']— a kit without races/classes/levels has nothing to put here:105-106hpis recomputed viacalculate_hp(class, level, con_modifier)— a hit-die lookup keyed on class name, defaulting to d8. It overwrites whatever HP the kit's own rules produced:110savesis derived from D&D class proficiencies — a concept many kits don't have:121"gold": character_data.get('gold', 0)is injected unconditionallyThe
goldone is the sharpest example of the mismatch: a kit can legitimately have no currency at all (an honour economy, barter, tripods and cattle), and this adds a gold field to every sheet regardless.Repro
ruleset.jsondeclares its own vitals, e.g."stat_schema": { "attributes": ["bia","podes","thymos","metis","noos","mythos","aidos"], "vitals": ["hp","menos","honour","hubris","miasma","kleos"] }goldfield.Net effect: a non-5e PC has to be written to the campaign's
character.jsondirectly, bypassing the supported path entirely.Suggested fix
Make it kit-aware the same way the rest of the runtime can be — read
stat_schema.vitalsfrom the active campaign'sruleset.json:namewhen the kit declares its own vitals; keep the current 5e requirement when it doesn't (so existing D&D campaigns are unchanged);calculate_hp/calculate_savesonly for 5e kits, and otherwise persist the sheet's own vitals untouched;goldunless the kit actually has it.I've done the equivalent for
player_manager.show_player/show_all_players(which printedLevel 1 ... Gold: 0for every kit) andschemas.validate_character(which required race/class/level of every character) in my fork, gating onstat_schema.vitalsand leaving the legacy path byte-for-byte identical when a kit declares none. Happy to open a PR here forsave_character.pyalong the same lines if that'd be useful — just didn't want to send one unsolicited.Related design question
The
/create-charactercommand itself is 5e-shaped end to end (races, classes, spell slots, gold), so fixing the save path alone still leaves the interview assuming D&D. Might be worth deciding whether the command should branch on the active kit, or whether a World Kit should be able to supply its own creation flow.Environment: Windows 11, Python 3.11,
mainas of today.