D&D: codified bestiary tuning pass — derive tuned roster from SRD staging

Adds `gen tuned` to cmd/open5e-import: a deterministic formula that scales
every raw SRD stat block down to an engine-ready DnDMonsterTemplate. HP/AC/
AttackBonus are verbatim SRD (AC clamped to the engine min 10); the Attack
stat is interpolated from attackByCRPoints, a CR→Attack anchor table lifted
from the hand-tuned dndBestiary (CR is the calibration axis the 2026-05-10
rebalance used — raw SRD per-hit damage is ignored). Speed/BlockRate are
coarse baselines from SpeedWalk/AC.

bestiary_tuned.go merges the 322 generated templates into dndBestiary, but
hand-authored roster entries win — the merge only fills IDs the roster does
not already define, so playtested numbers and wired abilities are untouched.

Abilities are deliberately not wired: every generated entry has a nil
Ability, with the SRD multiattack/trait text parked in Notes as raw material
for the follow-up ability-wiring pass.
This commit is contained in:
prosolis
2026-05-14 16:45:09 -07:00
parent 53be17e6fe
commit 908e2b0855
5 changed files with 684 additions and 1 deletions

View File

@@ -8,6 +8,7 @@
// open5e-import gen spells # classify → internal/plugin/dnd_spells_srd_data.go
// open5e-import fetch bestiary # vendor data/open5e/monsters.json from the API
// open5e-import gen bestiary # classify → internal/plugin/bestiary_srd_data.go
// open5e-import gen tuned # tuning formula → internal/plugin/bestiary_tuned_data.go
//
// `equipment` subcommands are planned; the dispatch below is structured to
// grow into them without reshuffling.
@@ -31,6 +32,7 @@ const (
monstersAPIBase = "https://api.open5e.com/v1/monsters/?document__slug=wotc-srd&limit=50"
monstersJSON = "data/open5e/monsters.json"
bestiaryGenGo = "internal/plugin/bestiary_srd_data.go"
tunedGenGo = "internal/plugin/bestiary_tuned_data.go"
)
func main() {
@@ -54,6 +56,8 @@ func main() {
must(genSpells())
case "bestiary":
must(genBestiary())
case "tuned":
must(genTuned())
default:
usage()
}
@@ -63,7 +67,7 @@ func main() {
}
func usage() {
fmt.Fprintln(os.Stderr, "usage: open5e-import (fetch|gen) (spells|bestiary)")
fmt.Fprintln(os.Stderr, "usage: open5e-import fetch (spells|bestiary) | gen (spells|bestiary|tuned)")
os.Exit(2)
}