package topdata import ( "fmt" "path/filepath" "slices" "strings" ) // validateClassFeatMasterfeatAccessibility warns when a class feat table // references a masterfeat whose member feats are silently dropped from // expansion by classFeatExpansionCanUseFeat. Only the ambiguous case is // flagged: ALLCLASSESCANUSE=0 with no MinLevelClass, where the feat cannot // be obtained by any class at all. Exclusions with an explicit MinLevelClass // are deliberate cross-class restrictions and stay silent. func validateClassFeatMasterfeatAccessibility(dataDir string, report *ValidationReport) { datasets, err := discoverNativeDatasets(dataDir) if err != nil { return } keyToID := map[string]int{} var featRowByKey map[string]map[string]any type classFeatTable struct { path string classKey string rows []map[string]any } classTables := []classFeatTable{} for _, dataset := range datasets { name := filepath.ToSlash(dataset.Name) isClassFeats := strings.HasPrefix(name, "classes/feats/") if name != "feat" && name != "masterfeats" && name != "classes/core" && !isClassFeats { continue } collected, err := collectNativeDataset(dataset) if err != nil { return } for key, id := range collected.LockData { keyToID[key] = id } switch { case name == "feat": featRowByKey = rowsByKey(collected.Rows) case isClassFeats: classTables = append(classTables, classFeatTable{ path: dataset.BasePath, classKey: "classes:" + name[strings.LastIndex(name, "/")+1:], rows: collected.Rows, }) } } if featRowByKey == nil { return } for _, table := range classTables { for _, row := range table.rows { featRef, ok := row["FeatIndex"].(map[string]any) if !ok { continue } refKey, _ := featRef["id"].(string) if !strings.HasPrefix(refKey, "masterfeats:") { continue } masterfeatKey := resolveCanonicalDatasetKey(refKey, keyToID) if _, ok := keyToID[masterfeatKey]; !ok { continue } featKeys := make([]string, 0, len(featRowByKey)) for featKey := range featRowByKey { if strings.HasPrefix(featKey, "feat:") { featKeys = append(featKeys, featKey) } } slices.Sort(featKeys) for _, featKey := range featKeys { featRow := featRowByKey[featKey] if !rowReferencesMasterfeat(featRow, masterfeatKey, keyToID) { continue } if classFeatExpansionCanUseFeat(featRow, table.classKey, keyToID) { continue } if minLevelClass, ok := lookupField(featRow, "MinLevelClass"); ok && !isNullishValue(minLevelClass) { continue } report.Diagnostics = append(report.Diagnostics, Diagnostic{ Severity: SeverityWarning, Path: table.path, Message: fmt.Sprintf( "%s references %s but %s is excluded from expansion (ALLCLASSESCANUSE=0 with no MinLevelClass makes it unobtainable); override ALLCLASSESCANUSE or set MinLevelClass", table.classKey, masterfeatKey, featKey), }) } } } }