Adv 2.0 D&D Phase 10 SUB1: subclass selection system

Implements the identity layer of gogobee_subclass_system.md: 15 subclasses
(3 per class) selectable at L5 via !subclass.

- Schema: new subclass + last_subclass_respec_at columns on dnd_character.
- Registry (dnd_subclass.go): 15 entries with class, display, tagline, and
  TwinBee flavor line; helpers for prompt rendering and input resolution
  (number, id, or display-name; case/space/hyphen-insensitive).
- !subclass command: bare form prints prompt or current; !subclass <name>
  selects (free first time) or changes (500 euros + 30-day cooldown via
  separate timestamp from the full !respec wipe). Save-then-debit ordering
  mirrors !respec audit fix B.
- Level-up DM at L5+ now embeds the real selection prompt instead of the
  Phase 9 placeholder; suppressed once a subclass is set.
- Sheet shows the subclass below the class line, or nudges !subclass when
  unchosen at L5+.
- Existing !respec full-wipe also clears subclass + cooldown timestamp.

Mechanical effects of L5/7/10/15 subclass abilities deferred to SUB2/SUB3.
This commit is contained in:
prosolis
2026-05-08 09:18:52 -07:00
parent b53e516bf0
commit 1ee4276bcd
9 changed files with 758 additions and 9 deletions

View File

@@ -173,6 +173,11 @@ func runMigrations(d *sql.DB) error {
`ALTER TABLE dnd_character ADD COLUMN pending_cast TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE dnd_character ADD COLUMN concentration_spell TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE dnd_character ADD COLUMN concentration_expires_at DATETIME`,
// Phase 10 — subclass system. Subclass id is set at L5 via !subclass.
// last_subclass_respec_at gates the 30-day cooldown for changing it
// (separate from last_respec_at, which gates the full character wipe).
`ALTER TABLE dnd_character ADD COLUMN subclass TEXT NOT NULL DEFAULT ''`,
`ALTER TABLE dnd_character ADD COLUMN last_subclass_respec_at DATETIME`,
}
for _, stmt := range columnMigrations {
if _, err := d.Exec(stmt); err != nil {