Combat: per-round !cast / !consume in turn-based fights

Turn-based Elite/Boss fights gain !cast and !consume as player-turn
actions, so casters and item-users make decisions per round instead of
pre-queuing a single effect. The command handler validates and resolves
the spell/item into a pre-rolled turnActionEffect; the engine just
applies the HP deltas and flows on into the enemy turn.

Scoped to effects that resolve within the casting round: damage, heal,
and control spells, plus heal/flat-damage consumables. Buff and utility
spells and buff-type consumables are refused without spending the
resource — they need cross-round stat persistence, a later sub-phase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-14 06:49:14 -07:00
parent a0961fee8a
commit 5cd343af0c
7 changed files with 563 additions and 21 deletions

View File

@@ -61,6 +61,13 @@ func decodePendingCast(s string) (PendingCast, bool) {
// ── !cast command ────────────────────────────────────────────────────────────
func (p *AdventurePlugin) handleDnDCastCmd(ctx MessageContext, args string) error {
// In a turn-based Elite/Boss fight, !cast resolves as the player's turn
// for the round rather than queuing for "next combat". handleCombatCastCmd
// takes the per-user lock itself and re-checks the session under it.
if sess, _ := getActiveCombatSession(ctx.Sender); sess != nil {
return p.handleCombatCastCmd(ctx, args)
}
userMu := p.advUserLock(ctx.Sender)
userMu.Lock()
defer userMu.Unlock()