mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Ship-audit P0/P1 sweep on open5e: prose, combat math, UX hygiene
P0 — actual bugs: - eldritch_blast: SRD-imported "Whatever" placeholder leaked to players; overlay added in dnd_spells_data.go. - Battle Master disarming_attack + parry: math.Max on multiplicative DamageReduct was a no-op vs the 1.0 default. Switched to *= and added regression tests covering the stacking case too. - Extended TestSpellDescriptionsAreJargonFree to catch "Whatever", trailing ellipses, mid-word truncation, and empty-size phrases. This surfaced 4 more genuinely-broken SRD descriptions (find_familiar, clairvoyance, glyph_of_warding, teleportation_circle); all now overlayed in dnd_spells_data.go. P1 — UX hygiene: - Setup completion now nudges new casters toward !spells/!cast. - Magic-item attunement vocab unified to "bond" everywhere user-facing; renamed the Treasures sheet section to avoid clashing with the bond vocab. - Ambient DM footers stripped of "Threat +N" / "Supplies −N SU" hidden- meter leakage; verb-form footers instead. - !expedition status defaults to a days-left + threat-label summary; raw SU / threat-out-of-100 / zone-stack / roll-modifier moved behind `!expedition status --debug`. - !zone taunt/compliment help text dropped the "(mood −10)" / "(mood +5)" numerics that turned the hidden TwinBee-mood mechanic into a min/max knob. - TwinBee pronoun pass: switched she/her to singular they on the two surfaces I touched plus achievements.bj_beat_twinbee. P1 — correctness/scale: - adventure_shop curio purchase now guards the euro.Debit bool return before inventory grant + pot cut; the GetBalance preflight isn't enough since a concurrent blackjack/lottery debit can slip the balance across BLACKJACK_DEBT_LIMIT between read and write. - equipMagicItem reordered: remove inventory item BEFORE equip, with a best-effort rollback if equip fails. The prior order left a dup window if the inventory delete tripped on a transient DB error. - magicItemsByRarityCache promoted to sync.Once — concurrent cold-start loot rolls otherwise raced on the lazy assignment. Stale-audit verifications (no code change needed): - light / druidcraft / water_walk overlays already exist and win the SRD merge — the broken raw text never reaches players. - bootstrapPhase5BHPRefresh is already wired at adventure.go:228. Deferred: - combatantsForSession per-turn 6-load DB chatter (perf P1-A): touches the hot combat path; needs its own test pass before shipping. Tracked in memory at project_combat_session_cache_deferred. go vet clean; go test -race ./... green (229s). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,7 @@ func (p *AdventurePlugin) handleDnDExpeditionCmd(ctx MessageContext, args string
|
||||
case "":
|
||||
// If active, show status; otherwise help.
|
||||
if exp, _ := getActiveExpedition(ctx.Sender); exp != nil {
|
||||
return p.expeditionCmdStatus(ctx)
|
||||
return p.expeditionCmdStatus(ctx, "")
|
||||
}
|
||||
return p.SendDM(ctx.Sender, expeditionHelpText())
|
||||
case "help", "?":
|
||||
@@ -59,7 +59,7 @@ func (p *AdventurePlugin) handleDnDExpeditionCmd(ctx MessageContext, args string
|
||||
case "start", "begin", "go":
|
||||
return p.expeditionCmdStart(ctx, c, rest)
|
||||
case "status", "info":
|
||||
return p.expeditionCmdStatus(ctx)
|
||||
return p.expeditionCmdStatus(ctx, rest)
|
||||
case "log", "history":
|
||||
return p.expeditionCmdLog(ctx)
|
||||
case "abandon", "quit":
|
||||
@@ -271,7 +271,18 @@ func estimateDays(maxSU, dailyBurn float32) int {
|
||||
|
||||
// ── status ──────────────────────────────────────────────────────────────────
|
||||
|
||||
func (p *AdventurePlugin) expeditionCmdStatus(ctx MessageContext) error {
|
||||
func (p *AdventurePlugin) expeditionCmdStatus(ctx MessageContext, args string) error {
|
||||
debug := false
|
||||
for _, tok := range strings.Fields(args) {
|
||||
switch strings.ToLower(tok) {
|
||||
case "--debug", "-d", "debug", "raw":
|
||||
debug = true
|
||||
}
|
||||
}
|
||||
return p.expeditionCmdStatusImpl(ctx, debug)
|
||||
}
|
||||
|
||||
func (p *AdventurePlugin) expeditionCmdStatusImpl(ctx MessageContext, debug bool) error {
|
||||
exp, err := getActiveExpedition(ctx.Sender)
|
||||
if err != nil {
|
||||
return p.SendDM(ctx.Sender, "Couldn't read expedition state: "+err.Error())
|
||||
@@ -298,21 +309,36 @@ func (p *AdventurePlugin) expeditionCmdStatus(ctx MessageContext) error {
|
||||
if c != nil {
|
||||
b.WriteString(fmt.Sprintf("❤️ **HP:** %d / %d\n", c.HPCurrent, c.HPMax))
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("🎒 **Supplies:** %.1f / %.1f SU _(burn %.1f/day → ~%d days left)_\n",
|
||||
exp.Supplies.Current, exp.Supplies.Max, currentBurn(exp),
|
||||
estimateDays(exp.Supplies.Current, currentBurn(exp))))
|
||||
b.WriteString(fmt.Sprintf("⏰ **Threat:** %d / 100 — %s\n",
|
||||
exp.ThreatLevel, threatThresholdLabel(exp.ThreatLevel, exp.SiegeMode)))
|
||||
if exp.TemporalStack != 0 {
|
||||
b.WriteString(fmt.Sprintf("🌡 **Zone stack:** %d\n", exp.TemporalStack))
|
||||
// Default view is verb/outcome-led: days-left summary + threat label,
|
||||
// no raw SU / threat-out-of-100 / zone-stack / roll-modifier numbers.
|
||||
// Pass `--debug` to !expedition status for the engineering view.
|
||||
days := estimateDays(exp.Supplies.Current, currentBurn(exp))
|
||||
if debug {
|
||||
b.WriteString(fmt.Sprintf("🎒 **Supplies:** %.1f / %.1f SU _(burn %.1f/day → ~%d days left)_\n",
|
||||
exp.Supplies.Current, exp.Supplies.Max, currentBurn(exp), days))
|
||||
b.WriteString(fmt.Sprintf("⏰ **Threat:** %d / 100 — %s\n",
|
||||
exp.ThreatLevel, threatThresholdLabel(exp.ThreatLevel, exp.SiegeMode)))
|
||||
if exp.TemporalStack != 0 {
|
||||
b.WriteString(fmt.Sprintf("🌡 **Zone stack:** %d\n", exp.TemporalStack))
|
||||
}
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("🎒 **Supplies:** ~%d day%s left\n",
|
||||
days, plural(days)))
|
||||
b.WriteString(fmt.Sprintf("⏰ **Threat:** %s\n",
|
||||
threatThresholdLabel(exp.ThreatLevel, exp.SiegeMode)))
|
||||
}
|
||||
if exp.Camp != nil && exp.Camp.Active {
|
||||
b.WriteString(fmt.Sprintf("⛺ **Camp:** %s (room %d)\n", exp.Camp.Type, exp.Camp.RoomIndex+1))
|
||||
}
|
||||
state := supplyDepletion(exp.Supplies)
|
||||
if state != SupplyNormal {
|
||||
b.WriteString(fmt.Sprintf("⚠ **%s** — roll modifier %d\n",
|
||||
depletionLabel(state), supplyRollModifier(state)))
|
||||
if debug {
|
||||
b.WriteString(fmt.Sprintf("⚠ **%s** — roll modifier %d\n",
|
||||
depletionLabel(state), supplyRollModifier(state)))
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("⚠ **%s** — you're slower and clumsier than usual.\n",
|
||||
depletionLabel(state)))
|
||||
}
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("\nStarted: %s Last activity: %s",
|
||||
exp.StartDate.Format("2006-01-02 15:04"),
|
||||
|
||||
Reference in New Issue
Block a user