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

@@ -19,16 +19,13 @@ type advPendingHospitalPay struct {
// hospitalCostsForUser returns (beforeInsurance, afterInsurance) for the
// hospital revival bill. Phase L4a switches the formula off CombatLevel
// onto DnDCharacter.Level: `Level × 50_000` after insurance, 5× that
// before. Falls back to the legacy combat-level mapping when no D&D
// character row exists yet (createDnDCharacterFromAdv hasn't run).
// before. Post-L5g every legacy player has a DnDCharacter row, so the
// CombatLevel-derived fallback has been retired. Floors at level 1.
func hospitalCostsForUser(char *AdventureCharacter) (int64, int64) {
level := 0
if dnd, err := LoadDnDCharacter(char.UserID); err == nil && dnd != nil {
level := 1
if dnd, err := LoadDnDCharacter(char.UserID); err == nil && dnd != nil && dnd.Level > 0 {
level = dnd.Level
}
if level <= 0 {
level = dndLevelFromCombatLevel(char.CombatLevel)
}
after := int64(level) * 50_000
before := after * 5
return before, after