diff --git a/internal/topdata/wiki_native_test.go b/internal/topdata/wiki_native_test.go index 77e4635..d898e12 100644 --- a/internal/topdata/wiki_native_test.go +++ b/internal/topdata/wiki_native_test.go @@ -727,6 +727,61 @@ datasets: } } +func TestWikiVisibilityIndexUsesBuiltVisibilityForReferenceHelpers(t *testing.T) { + policy, err := parseWikiVisibilityDefinitions([]byte(` +datasets: + feat: + include: + - when: "{{true}}" + exclude: + - when: "{{hidden_ref(PREREQFEAT1, \"feat\")}}" +`), "visibility.yaml") + if err != nil { + t.Fatalf("parse visibility policy: %v", err) + } + ctx := &wikiContext{ + visibilityPolicy: &policy, + featRows: map[string]map[string]any{ + "feat:zbase": { + "FEAT": "Base Feat", + "DESCRIPTION": "Description", + }, + "feat:dependent": { + "FEAT": "Dependent Feat", + "DESCRIPTION": "Description", + "PREREQFEAT1": 1, + }, + "feat:chain_dependent": { + "FEAT": "Chain Dependent Feat", + "DESCRIPTION": "Description", + "PREREQFEAT1": 2, + }, + "feat:missing_prereq": { + "FEAT": "Missing Prereq Feat", + "DESCRIPTION": "Description", + "PREREQFEAT1": 999, + }, + }, + featIDToKey: map[int]string{1: "feat:zbase", 2: "feat:dependent"}, + } + visible, err := ctx.buildWikiVisibilityIndex(&policy) + if err != nil { + t.Fatalf("build visibility index: %v", err) + } + if !visible["feat:zbase"] { + t.Fatalf("expected base feat to be visible") + } + if !visible["feat:dependent"] { + t.Fatalf("expected feat with visible prerequisite to remain visible") + } + if !visible["feat:chain_dependent"] { + t.Fatalf("expected feat with transitive visible prerequisite to remain visible") + } + if visible["feat:missing_prereq"] { + t.Fatalf("expected feat with missing prerequisite to be hidden") + } +} + func TestWikiVisibilityMetadataOverrideCannotBypassEligibility(t *testing.T) { policy, err := parseWikiVisibilityDefinitions([]byte(` datasets: diff --git a/internal/topdata/wiki_visibility.go b/internal/topdata/wiki_visibility.go index 729aa20..fff9e5c 100644 --- a/internal/topdata/wiki_visibility.go +++ b/internal/topdata/wiki_visibility.go @@ -187,21 +187,35 @@ func (ctx *wikiContext) buildWikiVisibilityIndex(policy *wikiVisibilityDefinitio } } referenceSets := ctx.collectWikiVisibilityReferenceSets(policy, visible) - for _, dataset := range []string{"classes", "racialtypes", "skills", "spells", "baseitems", "feat"} { - definition, ok := policy.Datasets[dataset] - if !ok { - continue - } - rows := ctx.wikiRowsForDataset(dataset) - for _, key := range sortedKeys(rows) { - include, err := ctx.evaluateWikiVisibility(dataset, key, rows[key], definition, referenceSets) - if err != nil { - return nil, err + previousVisible := ctx.visibleWikiKeys + ctx.visibleWikiKeys = visible + defer func() { + ctx.visibleWikiKeys = previousVisible + }() + for pass := 0; pass <= len(visible); pass++ { + changed := false + for _, dataset := range []string{"classes", "racialtypes", "skills", "spells", "baseitems", "feat"} { + definition, ok := policy.Datasets[dataset] + if !ok { + continue } - visible[key] = include + rows := ctx.wikiRowsForDataset(dataset) + for _, key := range sortedKeys(rows) { + include, err := ctx.evaluateWikiVisibility(dataset, key, rows[key], definition, referenceSets) + if err != nil { + return nil, err + } + if visible[key] != include { + changed = true + } + visible[key] = include + } + } + if !changed { + return visible, nil } } - return visible, nil + return nil, fmt.Errorf("wiki visibility index did not converge") } func (ctx *wikiContext) evaluateWikiVisibilityWithoutSets(dataset, key string, row map[string]any, definition wikiVisibilityDataset) (bool, error) {