mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
Adv 2.0 D&D Phase 11 D3c: Tier 2 zone flavor files
New additive flavor files (zone_forest_shadows_flavor.go,
zone_sunken_temple_flavor.go) with elite-room intros, boss ability
callouts, and zone lore. Reuses existing RoomEntryForestShadows and
BossEntryHollowKing from twinbee_gm_flavor.go. The Sunken Temple had
no canonical RoomEntry/BossEntry pools yet, so both are defined in the
new zone file.
Hollow King: Corrupting Aura / Root Surge / Devour Light / Phase Two
Dreaming Aboleth: Tentacle Multiattack / Enslave / Mucus Cloud /
Legendary Actions
Wires zoneRoomEntryPool, bossEntryPool, zoneLorePool,
bossSignaturePool, and eliteRoomEntryPool to the new tier-2 pools.
Updates the D2b "no callout pool" test to use Dragon's Lair (still
unflavored) and adds Tier 2 routing tests for boss-entry, elite line,
lore, and room-entry overlays.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,8 @@ func zoneRoomEntryPool(zoneID ZoneID) []string {
|
||||
return append(append([]string{}, flavor.RoomEntryCryptValdris...), flavor.RoomEntryGeneric...)
|
||||
case ZoneForestShadows:
|
||||
return append(append([]string{}, flavor.RoomEntryForestShadows...), flavor.RoomEntryGeneric...)
|
||||
case ZoneSunkenTemple:
|
||||
return append(append([]string{}, flavor.RoomEntrySunkenTemple...), flavor.RoomEntryGeneric...)
|
||||
case ZoneManorBlackspire:
|
||||
return append(append([]string{}, flavor.RoomEntryHauntedManor...), flavor.RoomEntryGeneric...)
|
||||
case ZoneUnderdark:
|
||||
@@ -97,6 +99,8 @@ func bossEntryPool(zoneID ZoneID) []string {
|
||||
return flavor.BossEntryValdris
|
||||
case ZoneForestShadows:
|
||||
return flavor.BossEntryHollowKing
|
||||
case ZoneSunkenTemple:
|
||||
return flavor.BossEntryDreamingAboleth
|
||||
case ZoneDragonsLair:
|
||||
return flavor.BossEntryInfernax
|
||||
case ZoneAbyssPortal:
|
||||
@@ -147,6 +151,10 @@ func zoneLorePool(zoneID ZoneID) []string {
|
||||
return append(append([]string{}, flavor.LoreLinesWarrens...), flavor.LoreLines...)
|
||||
case ZoneCryptValdris:
|
||||
return append(append([]string{}, flavor.LoreLinesCrypt...), flavor.LoreLines...)
|
||||
case ZoneForestShadows:
|
||||
return append(append([]string{}, flavor.LoreLinesForestShadows...), flavor.LoreLines...)
|
||||
case ZoneSunkenTemple:
|
||||
return append(append([]string{}, flavor.LoreLinesSunkenTemple...), flavor.LoreLines...)
|
||||
}
|
||||
return flavor.LoreLines
|
||||
}
|
||||
@@ -160,6 +168,10 @@ func bossSignaturePool(zoneID ZoneID) []string {
|
||||
return flavor.GrolSignatureCallouts
|
||||
case ZoneCryptValdris:
|
||||
return flavor.ValdrisSignatureCallouts
|
||||
case ZoneForestShadows:
|
||||
return flavor.HollowKingSignatureCallouts
|
||||
case ZoneSunkenTemple:
|
||||
return flavor.AbolethSignatureCallouts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -172,6 +184,10 @@ func eliteRoomEntryPool(zoneID ZoneID) []string {
|
||||
return flavor.EliteRoomEntryWarrens
|
||||
case ZoneCryptValdris:
|
||||
return flavor.EliteRoomEntryCrypt
|
||||
case ZoneForestShadows:
|
||||
return flavor.EliteRoomEntryForestShadows
|
||||
case ZoneSunkenTemple:
|
||||
return flavor.EliteRoomEntrySunkenTemple
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -186,14 +186,14 @@ func TestComposeBossEntry_AppendsAbilityCalloutForTier1(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestComposeBossEntry_NoCalloutForUnflavoredZone(t *testing.T) {
|
||||
// Forest of Shadows has a named boss-entry pool (Hollow King) but no
|
||||
// Dragon's Lair has a named boss-entry pool (Infernax) but no
|
||||
// signature-callout pool yet — composer should return the bare entry.
|
||||
got := composeBossEntry(ZoneForestShadows, "run-d2b", 1)
|
||||
got := composeBossEntry(ZoneDragonsLair, "run-d2b", 1)
|
||||
if got == "" {
|
||||
t.Fatal("forest boss-entry should still render")
|
||||
t.Fatal("dragon's lair boss-entry should still render")
|
||||
}
|
||||
if n := strings.Count(got, "🎭 **TwinBee:**"); n != 1 {
|
||||
t.Errorf("Forest boss-entry expected 1 TwinBee block (no callout pool), got %d:\n%s", n, got)
|
||||
t.Errorf("Dragon's Lair boss-entry expected 1 TwinBee block (no callout pool), got %d:\n%s", n, got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,13 +229,71 @@ func TestZoneLorePool_PrependsZoneSpecific(t *testing.T) {
|
||||
t.Errorf("Warrens lore pool should be larger than generic; got %d vs generic %d",
|
||||
len(w), len(flavor.LoreLines))
|
||||
}
|
||||
// Tier 2+ zone with no dedicated lore must fall back to the generic pool.
|
||||
if got := zoneLorePool(ZoneForestShadows); len(got) != len(flavor.LoreLines) {
|
||||
t.Errorf("Forest lore pool should fall back to generic; got %d, want %d",
|
||||
// Tier 3+ zone with no dedicated lore must fall back to the generic pool.
|
||||
if got := zoneLorePool(ZoneDragonsLair); len(got) != len(flavor.LoreLines) {
|
||||
t.Errorf("Dragon's Lair lore pool should fall back to generic; got %d, want %d",
|
||||
len(got), len(flavor.LoreLines))
|
||||
}
|
||||
}
|
||||
|
||||
// ── Phase 11 D3c — Tier 2 zone flavor file routing ──────────────────────────
|
||||
|
||||
func TestComposeBossEntry_AppendsAbilityCalloutForTier2(t *testing.T) {
|
||||
got := composeBossEntry(ZoneForestShadows, "run-d3c", 5)
|
||||
if !strings.Contains(got, "Hollow King") {
|
||||
t.Errorf("Forest boss-entry should mention Hollow King: %q", got)
|
||||
}
|
||||
if n := strings.Count(got, "🎭 **TwinBee:**"); n != 2 {
|
||||
t.Errorf("Forest boss-entry expected 2 TwinBee blocks (entry+callout), got %d:\n%s", n, got)
|
||||
}
|
||||
|
||||
got = composeBossEntry(ZoneSunkenTemple, "run-d3c", 5)
|
||||
if !strings.Contains(got, "Aboleth") {
|
||||
t.Errorf("Sunken Temple boss-entry should mention Aboleth: %q", got)
|
||||
}
|
||||
if n := strings.Count(got, "🎭 **TwinBee:**"); n != 2 {
|
||||
t.Errorf("Sunken Temple boss-entry expected 2 TwinBee blocks (entry+callout), got %d:\n%s", n, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEliteRoomEntryLine_Tier2Routes(t *testing.T) {
|
||||
f := eliteRoomEntryLine(ZoneForestShadows, "run-elite-t2", 3)
|
||||
if f == "" || !strings.HasPrefix(f, "🎭 **TwinBee:**") {
|
||||
t.Errorf("Forest elite line missing/no prefix: %q", f)
|
||||
}
|
||||
s := eliteRoomEntryLine(ZoneSunkenTemple, "run-elite-t2", 3)
|
||||
if s == "" || !strings.HasPrefix(s, "🎭 **TwinBee:**") {
|
||||
t.Errorf("Sunken Temple elite line missing/no prefix: %q", s)
|
||||
}
|
||||
if f == s {
|
||||
t.Errorf("Forest and Sunken Temple elite lines collided on same salt")
|
||||
}
|
||||
}
|
||||
|
||||
func TestZoneLorePool_Tier2PrependsZoneSpecific(t *testing.T) {
|
||||
f := zoneLorePool(ZoneForestShadows)
|
||||
if len(f) <= len(flavor.LoreLines) {
|
||||
t.Errorf("Forest lore pool should be larger than generic; got %d vs generic %d",
|
||||
len(f), len(flavor.LoreLines))
|
||||
}
|
||||
s := zoneLorePool(ZoneSunkenTemple)
|
||||
if len(s) <= len(flavor.LoreLines) {
|
||||
t.Errorf("Sunken Temple lore pool should be larger than generic; got %d vs generic %d",
|
||||
len(s), len(flavor.LoreLines))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoomEntryPool_Tier2PrependsZoneSpecific(t *testing.T) {
|
||||
f := zoneRoomEntryPool(ZoneForestShadows)
|
||||
if len(f) <= len(flavor.RoomEntryGeneric) {
|
||||
t.Errorf("Forest room-entry pool should overlay zone-specific lines; got %d", len(f))
|
||||
}
|
||||
s := zoneRoomEntryPool(ZoneSunkenTemple)
|
||||
if len(s) <= len(flavor.RoomEntryGeneric) {
|
||||
t.Errorf("Sunken Temple room-entry pool should overlay zone-specific lines; got %d", len(s))
|
||||
}
|
||||
}
|
||||
|
||||
func TestZoneCmd_AdvanceFinalRoomBumpsMood(t *testing.T) {
|
||||
setupAuditTestDB(t)
|
||||
uid := id.UserID("@zone-mood-complete:example")
|
||||
|
||||
Reference in New Issue
Block a user