UX S7: sheet + menu jargon sweep — outcome-first copy

Drops D&D-handbook syntax from the player-facing caster UX in favor of
verbs and feelings (per feedback_accessibility_over_dnd_crunch).

R12 — Drop "Spell DC: N / Spell Atk: +N" line from the spellbook view.
The numbers are pure handbook noise; the engine still computes them.

R13 — renderSlotLine swaps "L1 3/4 · L2 1/3" for "Level 1 ●●●○ · Level 2
●○○○". Filled bullets = energy left, hollow = spent.

R14 — Caster passive Description strings rewritten outcome-first: no
"+5%", no "scaled by your Charisma". Verbs and texture instead. All ten
class passives reworded; mechanics in applyClassPassives unchanged.

R18 — Class menu drops the "(d8, INT/WIS)" suffix → "**Mage** — INT &
WIS". The HP-die number was leaking implementation. !setup class confirm
line gets the same treatment.

P5 — Spellbook headers: "Cantrip" → "Cantrips", "L1" → "Level 1".

P6 — Cast queue line: "_(upcast to L2)_" → "_(empowered)_". Queued-line
in the spellbook view follows suit — drops "(L2 slot)" for "(empowered)"
when the slot is above the spell's base level.

P8 — Comment-convention marker added to dnd_passives.go and
dnd_subclass_combat.go file headers: `// internal note (not user-facing)`
flags Phase 2/3 tuning history so future codegen doesn't lift it into
Description / Flavor strings.

P9 — Spellbook line drops the duplicate "(can learn N more)" trailer —
the "%d / %d" already conveys remaining capacity.

Bonus cleanup. The two Arcane-Trickster slot-ceiling errors ("Arcane
Trickster L5 only has up to L1 slots.") and the "No L1 slot available"
out-of-energy messages get the same jargon scrub: "At level 5, your
Arcane Trickster magic only reaches level-1 spells." / "You're out of
level-1 energy."

Tests. Full plugin suite green; no test pinned the old strings.
This commit is contained in:
prosolis
2026-05-14 22:17:03 -07:00
parent 6386161402
commit 8ee170bb9b
6 changed files with 45 additions and 33 deletions

View File

@@ -147,7 +147,7 @@ func (p *AdventurePlugin) handleDnDCastCmd(ctx MessageContext, args string) erro
if c.Class == ClassRogue && c.Subclass == SubclassArcaneTrickster {
if max := highestAvailableSlotForChar(c); spell.Level > max {
return p.SendDM(ctx.Sender, fmt.Sprintf(
"Arcane Trickster L%d only has up to L%d slots.", c.Level, max))
"At level %d, your Arcane Trickster magic only reaches level-%d spells.", c.Level, max))
}
}
@@ -207,7 +207,7 @@ func (p *AdventurePlugin) handleDnDCastCmd(ctx MessageContext, args string) erro
}
if !ok {
return p.SendDM(ctx.Sender, fmt.Sprintf(
"No L%d slot available. %s", slotLevel, renderSlotsBrief(ctx.Sender)))
"You're out of level-%d energy. %s", slotLevel, renderSlotsBrief(ctx.Sender)))
}
}
@@ -342,7 +342,7 @@ func (p *AdventurePlugin) queuePendingCast(ctx MessageContext, c *DnDCharacter,
msg := fmt.Sprintf("✨ **%s** queued for next combat.", spell.Name)
if slotLevel > spell.Level {
msg += fmt.Sprintf(" _(upcast to L%d)_", slotLevel)
msg += " _(empowered)_"
}
if supersedes != "" {
msg += fmt.Sprintf("\n_(Concentration shifts: %s ended.)_", displaySpellName(supersedes))
@@ -438,9 +438,9 @@ func renderSpellsList(c *DnDCharacter) string {
}
sort.Ints(levels)
for _, lvl := range levels {
label := fmt.Sprintf("L%d", lvl)
label := fmt.Sprintf("Level %d", lvl)
if lvl == 0 {
label = "Cantrip"
label = "Cantrips"
}
b.WriteString(fmt.Sprintf("__%s__\n", label))
for _, k := range byLevel[lvl] {
@@ -460,23 +460,17 @@ func renderSpellsList(c *DnDCharacter) string {
slots, _ := getSpellSlots(c.UserID)
b.WriteString("\n**Slots:** " + renderSlotLine(slots) + "\n")
b.WriteString(fmt.Sprintf("**Spell DC:** %d **Spell Atk:** +%d\n",
spellSaveDC(c), spellAttackBonus(c)))
if c.Class == ClassMage {
count, _ := mageLeveledKnownCount(c.UserID)
cap := mageKnownSpellsCap(c.Level)
b.WriteString(fmt.Sprintf("**Spellbook:** %d/%d leveled spells", count, cap))
if avail := cap - count; avail > 0 {
b.WriteString(fmt.Sprintf(" _(can learn %d more)_", avail))
}
b.WriteString("\n")
b.WriteString(fmt.Sprintf("**Spellbook:** %d / %d leveled spells learned\n", count, cap))
}
if pc, ok := decodePendingCast(c.PendingCast); ok {
b.WriteString(fmt.Sprintf("\n_Queued: **%s**", displaySpellName(pc.SpellID)))
if pc.SlotLevel > 0 {
b.WriteString(fmt.Sprintf(" (L%d slot)", pc.SlotLevel))
if s, ok := lookupSpell(pc.SpellID); ok && pc.SlotLevel > s.Level {
b.WriteString(" (empowered)")
}
b.WriteString(" — fires next combat._\n")
}