Adv 2.0 D&D Phase 11 D8: mood-banded TwinBee aside

Adds a TwinBee aside on room entry when GM mood sits at the extreme
bands (≤19 hostile, ≥80 effusive). Mid-bands stay silent — the aside is
strictly extra flavor, never replacing core narration. Two new pools
(MoodAsidesHostile, MoodAsidesEffusive) live alongside the existing
TwinBee voice in twinbee_gm_flavor.go. Wired into zoneCmdEnter and
zoneCmdAdvance; the advance path reloads the run so post-combat
nat20/nat1 deltas are reflected before band lookup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-08 14:25:04 -07:00
parent 71594a0d89
commit 7bb922ae62
4 changed files with 138 additions and 0 deletions

View File

@@ -381,6 +381,36 @@ func complimentResponseLine(runID string, roomIdx int) string {
return "🎭 **TwinBee:** " + line
}
// moodAsidePool returns the mood-banded aside pool when the mood sits at
// an extreme band (hostile or effusive), else nil. Mid-bands (grumpy /
// neutral / friendly) intentionally surface no aside — flavor stays
// strictly extra, never replacing the core narration.
func moodAsidePool(mood int) []string {
switch moodBand(mood) {
case MoodBandHostile:
return flavor.MoodAsidesHostile
case MoodBandEffusive:
return flavor.MoodAsidesEffusive
}
return nil
}
// moodAsideLine renders a deterministic TwinBee aside reflecting the
// run's mood at the extreme bands. roomIdx is folded so the same room
// in the same run is stable; cross-room asides vary. Returns "" for
// mid-band moods.
func moodAsideLine(mood int, runID string, roomIdx int) string {
pool := moodAsidePool(mood)
if len(pool) == 0 {
return ""
}
line := pickLineDeterministic(pool, runID, roomIdx^0x6F2D8B14)
if line == "" {
return ""
}
return "🎭 **TwinBee:** " + line
}
// ── Mood event table & application ───────────────────────────────────────────
// GMMoodEvent enumerates the mood-modifier triggers from design doc §3.2.