Mechanics are rules and live in code, beside Luck and Currency. The skill pools are new — the races/classes spec said only 'the stat-appropriate slice' and never enumerated them; each pool is larger than its pick count so the choice is real. The dwarf's +2 vs poison is exposed separately from save() rather than folded into it: baking a conditional into a flat number is how a sheet starts lying. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QYa9u7Kdxv5gX4AnwWexy8
61 lines
1.9 KiB
GDScript
61 lines
1.9 KiB
GDScript
class_name Races
|
|
extends RefCounted
|
|
## The 4 races (races-and-classes spec §3). FEATURES ONLY — no race touches an
|
|
## attribute score, which is what keeps the roll + spend pure.
|
|
##
|
|
## nightsight / claws / keen_scent are FLAGS, not numbers. Combat (M5) and the DM's
|
|
## exploration checks consume them; creation only stores them.
|
|
##
|
|
## The dwarf's +2 is SITUATIONAL (vs poison and the affliction track), so it is NOT
|
|
## a flat save bonus — baking a conditional into a flat number is how a sheet starts
|
|
## lying. It is exposed separately as poison_save_bonus().
|
|
|
|
const IDS := ["human", "elf", "dwarf", "beastfolk"]
|
|
|
|
const TABLE := {
|
|
"human": {
|
|
"save_bonus": 1, "granted_skills": [], "bonus_skill_choice": true,
|
|
"poison_save_bonus": 0,
|
|
"flags": {"nightsight": false, "claws": false, "keen_scent": false},
|
|
},
|
|
"elf": {
|
|
"save_bonus": 0, "granted_skills": ["perception"], "bonus_skill_choice": false,
|
|
"poison_save_bonus": 0,
|
|
"flags": {"nightsight": true, "claws": false, "keen_scent": false},
|
|
},
|
|
"dwarf": {
|
|
"save_bonus": 0, "granted_skills": [], "bonus_skill_choice": false,
|
|
"poison_save_bonus": 2,
|
|
"flags": {"nightsight": true, "claws": false, "keen_scent": false},
|
|
},
|
|
"beastfolk": {
|
|
"save_bonus": 0, "granted_skills": [], "bonus_skill_choice": false,
|
|
"poison_save_bonus": 0,
|
|
"flags": {"nightsight": false, "claws": true, "keen_scent": true},
|
|
},
|
|
}
|
|
|
|
|
|
static func exists(id: String) -> bool:
|
|
return TABLE.has(id)
|
|
|
|
|
|
static func save_bonus(id: String) -> int:
|
|
return int(TABLE.get(id, {}).get("save_bonus", 0))
|
|
|
|
|
|
static func granted_skills(id: String) -> Array:
|
|
return TABLE.get(id, {}).get("granted_skills", [])
|
|
|
|
|
|
static func wants_bonus_skill(id: String) -> bool:
|
|
return bool(TABLE.get(id, {}).get("bonus_skill_choice", false))
|
|
|
|
|
|
static func poison_save_bonus(id: String) -> int:
|
|
return int(TABLE.get(id, {}).get("poison_save_bonus", 0))
|
|
|
|
|
|
static func flag(id: String, name: String) -> bool:
|
|
return bool(TABLE.get(id, {}).get("flags", {}).get(name, false))
|