Adv 2.0 L5g: DnDCharacter mass-backfill + drop CombatLevel fallbacks

backfillDnDCharactersFromAdv walks adventure_characters rows that have no
dnd_character row and inserts an auto-migrated character (race/class
inferred from archetypes, Level seeded from dndLevelFromCombatLevel).
Idempotent via LEFT JOIN — pending-setup drafts and existing rows are
skipped. Wired into Init after the L5f backfill.

With every legacy player guaranteed a D&D row, the soak-window fallbacks
in dndLevelForUser, rivalLevelForUser, and hospitalCostsForUser are
retired (they now floor at level 1). dndLevelFromCombatLevel itself
stays — still used by autoBuildCharacter and the !setup seed paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-09 11:41:46 -07:00
parent df886ab86f
commit 0d2e554e22
7 changed files with 186 additions and 23 deletions

View File

@@ -340,22 +340,16 @@ func dndLevelFromCombatLevel(combatLevel int) int {
return lvl
}
// dndLevelForUser returns the player's display-facing D&D level: the
// DnDCharacter row's Level when present, else the converted legacy
// adventure_characters.combat_level as a soak-period fallback. Used by
// render/twinbee/scheduler after L4f to keep "Combat Lv.X" in player-
// facing surfaces in sync with the D&D level system without leaking the
// AdventureCharacter type into render code.
// dndLevelForUser returns the player's display-facing D&D level. Post-L5g
// every legacy player has a DnDCharacter row (auto-migrated by the one-shot
// mass-backfill on Init), so the legacy CombatLevel fallback that lived here
// during the soak window has been retired. Returns 1 as a safe default for
// users without any D&D row (e.g. brand-new accounts before first combat).
func dndLevelForUser(userID id.UserID) int {
if c, err := LoadDnDCharacter(userID); err == nil && c != nil && c.Level > 0 {
return c.Level
}
row := db.Get().QueryRow(`SELECT combat_level FROM adventure_characters WHERE user_id = ?`, string(userID))
var legacy int
if err := row.Scan(&legacy); err != nil {
return 1
}
return dndLevelFromCombatLevel(legacy)
return 1
}
// applyRaceMods adds the race's ability modifiers to a base score block.