mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 00:52:40 +00:00
N5/D4: secret content pass — treasure-cache secret rooms + cross-zone keys
Secret rooms were dead content: every NodeKindSecret node silently collapsed to a normal exploration fight and its authored LootBias (1.5-3.0) was never read at runtime. D4 makes them what they read as — no-combat treasure caches. resolveRoom now diverts a secret node (keyed off the graph node, since CurrentRoomType has already lost the kind) to resolveSecretRoom before the RoomType switch — shared by manual !zone advance, !expedition run autopilot, and the sim. Each secret pays a guaranteed journal page (the D1a grant hook built "for secret rooms"), a LootBias-weighted treasure roll (floored at elite weight), and a guaranteed zone-tier consumable cache, with bespoke in-world discovery flavor. Underdark was the only T2+ zone with no secret; added at throne_gallery on the universal R4 tail (Lost Reliquary, Perception DC 17), merging to throne_steps so it's length-neutral. Two cross-zone keys: the Sunken Temple's Coral Reliquary grants a Sunken Sigil that opens a Sealed Reliquary in Manor Blackspire; the Underforge's Forge Vault grants an Underforge Seal that opens a Sealed Vault in the Underdark. Keys are persistent inventory items matched against LockKey key_id; grants are idempotent. Graph validator + no-soft-lock pass on both touched graphs; combat golden byte-identical; go build/vet/test green repo-wide. Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa
This commit is contained in:
@@ -12,22 +12,24 @@ func TestManorBlackspireGraph_Registered(t *testing.T) {
|
||||
}
|
||||
// Long-expedition D1-c widened this zone from 11 → 35 nodes so the
|
||||
// longest entry→boss walk lands in the T3 [22,26] traversal band.
|
||||
if len(g.Nodes) != 35 {
|
||||
t.Errorf("nodes = %d, want 35", len(g.Nodes))
|
||||
// N5/D4 added the Sealed Reliquary cross-zone vault → 36.
|
||||
if len(g.Nodes) != 36 {
|
||||
t.Errorf("nodes = %d, want 36", len(g.Nodes))
|
||||
}
|
||||
}
|
||||
|
||||
// TestManorBlackspireGraph_TwoStackedThreeWayForks captures the design
|
||||
// shape: both great_hall and upper_hall expose three options.
|
||||
func TestManorBlackspireGraph_TwoStackedThreeWayForks(t *testing.T) {
|
||||
// TestManorBlackspireGraph_StackedForks captures the design shape: great_hall
|
||||
// exposes three options; upper_hall exposes four since N5/D4 added the Sealed
|
||||
// Reliquary key spoke (the other three remain the elite/secret/tower gates).
|
||||
func TestManorBlackspireGraph_StackedForks(t *testing.T) {
|
||||
g := zoneManorBlackspireGraph()
|
||||
for _, hub := range []string{
|
||||
"manor_blackspire.great_hall",
|
||||
"manor_blackspire.upper_hall",
|
||||
} {
|
||||
outs := g.outgoingEdges(hub)
|
||||
if len(outs) != 3 {
|
||||
t.Errorf("%s outgoing = %d, want 3", hub, len(outs))
|
||||
want := map[string]int{
|
||||
"manor_blackspire.great_hall": 3,
|
||||
"manor_blackspire.upper_hall": 4,
|
||||
}
|
||||
for hub, n := range want {
|
||||
if outs := g.outgoingEdges(hub); len(outs) != n {
|
||||
t.Errorf("%s outgoing = %d, want %d", hub, len(outs), n)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +45,7 @@ func TestManorBlackspireGraph_AllSpokesReachBoss(t *testing.T) {
|
||||
"manor_blackspire.master_bedroom",
|
||||
"manor_blackspire.hidden_oratory",
|
||||
"manor_blackspire.tower_observatory",
|
||||
"manor_blackspire.sealed_reliquary",
|
||||
} {
|
||||
if !reachable(g, leaf, "manor_blackspire.boss") {
|
||||
t.Errorf("%s unreachable to boss", leaf)
|
||||
|
||||
Reference in New Issue
Block a user