diff --git a/internal/topdata/wiki_data_providers.go b/internal/topdata/wiki_data_providers.go index 05d2798..1618510 100644 --- a/internal/topdata/wiki_data_providers.go +++ b/internal/topdata/wiki_data_providers.go @@ -37,7 +37,7 @@ func loadWikiDataProviders(path string) (map[string]wikiDataProviderDefinition, return nil, fmt.Errorf("wiki data providers %s: provider %s missing source", path, name) } switch strings.TrimSpace(def.Source) { - case "class_progression", "class_feats", "class_summary", "race_feats", "race_name_forms": + case "class_progression", "class_feats", "class_skills", "class_summary", "race_feats", "race_name_forms": default: return nil, fmt.Errorf("wiki data providers %s: provider %s has unknown source %q", path, name, def.Source) } @@ -64,6 +64,11 @@ func (ctx *wikiContext) wikiDataProviderRows(name string, page wikiTemplatePage) return nil, nil } return ctx.classFeatRows(page.Row), nil + case "class_skills": + if page.Category != "classes" { + return nil, nil + } + return ctx.classSkillRows(page.Row), nil case "class_summary": if page.Category != "classes" { return nil, nil @@ -84,6 +89,31 @@ func (ctx *wikiContext) wikiDataProviderRows(name string, page wikiTemplatePage) } } +func (ctx *wikiContext) classSkillRows(classRow map[string]any) []map[string]any { + table := ctx.tableForValue(fieldValue(classRow, "SkillsTable"), ctx.classSkillTables) + if table == nil { + return nil + } + rows := []map[string]any{} + for _, source := range table.Rows { + if stringValue(source, "ClassSkill") != "1" { + continue + } + key := resolveReferenceKey(fieldValue(source, "SkillIndex"), ctx.skillIDToKey) + if key == "" { + key = resolveReferenceKey(fieldValue(source, "SkillIndex"), nil) + } + if key == "" || !ctx.canReferenceWikiKey(key) { + continue + } + row := cloneRowMap(source) + row["SkillKey"] = key + row["SkillName"] = ctx.resolveSkillName(key) + rows = append(rows, row) + } + return rows +} + func (ctx *wikiContext) raceFeatRows(raceRow map[string]any) []map[string]any { values := []any{} switch typed := fieldValue(raceRow, "FeatsTable").(type) { diff --git a/internal/topdata/wiki_native_test.go b/internal/topdata/wiki_native_test.go index baf9158..419bb9a 100644 --- a/internal/topdata/wiki_native_test.go +++ b/internal/topdata/wiki_native_test.go @@ -311,6 +311,99 @@ func TestWikiTemplateEachBlockRendersExplicitClassProgressionTable(t *testing.T) } } +func TestWikiTemplateEachBlockRendersClassSkillsAsIndividualListItems(t *testing.T) { + ctx := &wikiContext{ + wikiDataProviders: map[string]wikiDataProviderDefinition{ + "ClassSkills": {Source: "class_skills"}, + }, + classSkillTables: map[string]wikiTable{ + "classes/skills:fighter": { + Rows: []map[string]any{ + {"SkillIndex": "1", "ClassSkill": "1"}, + {"SkillIndex": "2", "ClassSkill": "1"}, + {"SkillIndex": "3", "ClassSkill": "0"}, + }, + }, + }, + skillIDToKey: map[int]string{ + 1: "skills:athletics", + 2: "skills:parry", + 3: "skills:secret", + }, + skillRows: map[string]map[string]any{ + "skills:athletics": {"Name": "Athletics"}, + "skills:parry": {"Name": "Parry"}, + "skills:secret": {"Name": "Secret"}, + }, + } + page := wikiTemplatePage{ + PageID: "classes:fighter", + Category: "classes", + Key: "classes:fighter", + Title: "Fighter", + Row: map[string]any{ + "SkillsTable": map[string]any{"table": "classes/skills:fighter"}, + }, + } + + template := `` + + got, err := ctx.renderWikiTemplateString("classes.html", template, page) + if err != nil { + t.Fatalf("render template: %v", err) + } + for _, want := range []string{ + `
  • [[skills/athletics|Athletics]]
  • `, + `
  • [[skills/parry|Parry]]
  • `, + } { + if !strings.Contains(got, want) { + t.Fatalf("expected rendered output to contain %q:\n%s", want, got) + } + } + for _, reject := range []string{"Athletics, ", "Secret"} { + if strings.Contains(got, reject) { + t.Fatalf("expected rendered output not to contain %q:\n%s", reject, got) + } + } +} + +func TestWikiTemplateListFilterRendersSliceAsListItems(t *testing.T) { + ctx := &wikiContext{} + page := wikiTemplatePage{ + PageID: "classes:fighter", + Category: "classes", + Key: "classes:fighter", + Title: "Fighter", + Row: map[string]any{ + "GrantedFeats": []string{ + "[[feat/literate|Literate]]", + "[[feat/armor-proficiency-light|Armor Proficiency (light)]]", + }, + }, + } + + got, err := ctx.renderWikiTemplateString("classes.html", `{{GrantedFeats|list:"wiki-list wiki-inline-list"}}`, page) + if err != nil { + t.Fatalf("render template: %v", err) + } + for _, want := range []string{ + `") + return out.String() +} + +func wikiTemplateListItems(value any) []string { + switch typed := value.(type) { + case []string: + out := make([]string, 0, len(typed)) + for _, item := range typed { + if text := strings.TrimSpace(item); text != "" { + out = append(out, text) + } + } + return out + case []any: + out := make([]string, 0, len(typed)) + for _, item := range typed { + if text := strings.TrimSpace(stringifyWikiTemplatePlain(item)); text != "" { + out = append(out, text) + } + } + return out + default: + if text := strings.TrimSpace(stringifyWikiTemplatePlain(value)); text != "" { + return []string{text} + } + return nil + } +} + func (ctx *wikiContext) renderTemplateReference(value any, dataset string) string { return ctx.renderReference(value, ctx.wikiIDMapForDataset(dataset), ctx.wikiNameResolverForDataset(dataset)) }