Adv 2.0 D&D polish: GM→DM rename, streamed zone combat, end-to-end scenario test

GM→DM rename across docs and code (GMNarrationType→DMNarrationType,
GMState→DMState, narration constants, comments) so the system reads as
"Dungeon Master" everywhere. Player-visible "GM mood" wording stays
where it appears in flavor.

Streamed zone/expedition combat: zone advance now stages patrol →
patrol play-by-play → patrol resolution → room intro → room play-by-play
→ final outcome through sendZoneCombatMessages with 2–3s pacing
(arena keeps its 5–8s window). Combat narrative lines pick up a compact
d20-vs-AC roll annotation for hit/crit/miss/block events.

Combat outcome polish: dndHPSnapshot lets narration show sheet HP
rather than legacy combat-engine HP, and markAdventureDead clears the
zombie state where hp_current was 0 but the legacy alive flag stayed
true after a D&D-layer KO.

Adv 2.0 announcement (ADVENTURE_2.0_ANNOUNCEMENT.md), README rewrite
covering the new layer, and adv2_scenario_test.go — a full
zone-run + expedition + harvest playthrough against a copy of the prod
DB asserting persisted state end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-09 07:43:11 -07:00
parent 83a71173b1
commit dfa7beeb96
32 changed files with 1262 additions and 334 deletions

View File

@@ -258,9 +258,9 @@ func TestFixedHolidays_NoDuplicateMonthDay(t *testing.T) {
func TestDailySummary_HolidayBlock(t *testing.T) {
players := []AdvPlayerDaySummary{
{DisplayName: "Alice", Activity: "dungeon", Location: "Cellar", Outcome: "success", LootValue: 500, HolidayActions: 2},
{DisplayName: "Bob", Activity: "mine", Location: "Quarry", Outcome: "success", LootValue: 300, HolidayActions: 1},
{DisplayName: "Carol", IsDead: true, DeadUntil: "14:00 UTC", Activity: "dungeon", Location: "Cave", Outcome: "death", HolidayActions: 1},
{DisplayName: "Alice", Activity: "dungeon", Location: "Cellar", Outcome: "success", LootValue: 500},
{DisplayName: "Bob", Activity: "mine", Location: "Quarry", Outcome: "success", LootValue: 300},
{DisplayName: "Carol", IsDead: true, DeadUntil: "14:00 UTC", Activity: "dungeon", Location: "Cave", Outcome: "death"},
}
text := renderAdvDailySummary("2026-12-25", nil, TwinBeeRewardSummary{}, players, "Christmas")
@@ -268,17 +268,19 @@ func TestDailySummary_HolidayBlock(t *testing.T) {
if !strings.Contains(text, "Christmas") {
t.Error("summary should contain holiday name")
}
if !strings.Contains(text, "two actions today") {
t.Error("summary should mention two actions on holidays")
// Adv 2.0 — holiday recap now advertises the +5 mood / free pack /
// +1 yield perks instead of the retired double-action mechanic.
if !strings.Contains(text, "TwinBee's blessing") {
t.Error("summary should mention TwinBee's blessing on holidays")
}
if !strings.Contains(text, "before their second action") {
t.Error("summary should note Carol died before second action")
if !strings.Contains(text, "free standard pack") {
t.Error("summary should mention the complimentary standard pack")
}
// Without holiday
textNoHol := renderAdvDailySummary("2026-12-26", nil, TwinBeeRewardSummary{}, players, "")
if strings.Contains(textNoHol, "two actions") {
t.Error("non-holiday summary should NOT mention two actions")
if strings.Contains(textNoHol, "TwinBee's blessing") {
t.Error("non-holiday summary should NOT mention TwinBee's blessing")
}
}