package cmd import ( "fmt" "strings" "github.com/runescape-wiki/rsw/internal/extract" "github.com/runescape-wiki/rsw/internal/render" "github.com/runescape-wiki/rsw/internal/wiki" "github.com/spf13/cobra" ) func newQuestCmd() *cobra.Command { return &cobra.Command{ Use: "quest ", Short: "Look up quest requirements, items needed, and rewards", Long: `Searches for a quest and displays its details: skill requirements, quest prerequisites, items needed, enemies to defeat, and rewards. With --ironman, flags items that need self-sufficient acquisition and notes which combat encounters might be dangerous for HCIM. Examples: rsw osrs quest "Monkey Madness I" rsw rs3 quest "Plague's End" --ironman`, Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { name := args[0] wikiClient := wiki.NewClient(GameBaseURL()) // Try fetching the page directly first (exact title match) page, err := wikiClient.GetPage(name) if err != nil { // Fall back to search results, err := wikiClient.Search(name, 5) if err != nil { return fmt.Errorf("search failed: %w", err) } if len(results) == 0 { return fmt.Errorf("no wiki page found for quest %q", name) } page, err = wikiClient.GetPage(results[0].Title) if err != nil { return fmt.Errorf("failed to fetch page: %w", err) } } if Raw() { fmt.Println(page.Wikitext) return nil } templates := extract.ParseTemplates(page.Wikitext) md := render.New() md.H1(page.Title) // Extract from Infobox Quest questBox := extract.FindTemplate(templates, "Infobox Quest") if questBox == nil { questBox = extract.FindTemplate(templates, "Infobox quest") } if questBox != nil { md.H2("Overview") md.KV("Members", questBox.Params["members"]) md.KV("Difficulty", questBox.Params["difficulty"]) md.KV("Length", questBox.Params["length"]) md.KV("Series", questBox.Params["series"]) md.KV("Age", questBox.Params["age"]) md.KV("Release", questBox.Params["release"]) md.Newline() } // Extract from Quest details template (OSRS stores requirements here) details := extract.FindTemplate(templates, "Quest details") if details == nil { details = extract.FindTemplate(templates, "Quest Details") } if details != nil { renderQuestTemplateField(md, details, "difficulty", "Difficulty") renderQuestTemplateField(md, details, "length", "Length") renderQuestTemplateField(md, details, "requirements", "Requirements") renderQuestTemplateField(md, details, "items", "Items Required") renderQuestTemplateField(md, details, "recommended", "Recommended") renderQuestTemplateField(md, details, "kills", "Enemies to Defeat") } // Also try section-based extraction as fallback renderQuestSection(md, page, wikiClient, page.Title, "Requirements", []string{"requirements", "skill requirements"}) renderQuestSection(md, page, wikiClient, page.Title, "Items Required", []string{"items required", "items needed", "required items"}) renderQuestSection(md, page, wikiClient, page.Title, "Enemies to Defeat", []string{"enemies to defeat", "enemies"}) renderQuestSection(md, page, wikiClient, page.Title, "Rewards", []string{"rewards"}) if Ironman() { md.HR() md.H2("Ironman Notes") md.P("*Consider the following for self-sufficient play:*") md.Bullet("All required items must be obtained without the GE") md.Bullet("Check drop sources and shop availability for each required item") md.Bullet("Boss/enemy encounters may be dangerous for HCIM — review combat levels and mechanics") md.Newline() } fmt.Print(md.String()) return nil }, } } func renderQuestTemplateField(md *render.Builder, details *extract.Infobox, field, heading string) { val := details.Params[field] if val == "" { return } cleaned := extract.CleanWikitext(val) cleaned = strings.TrimSpace(cleaned) if cleaned == "" { return } md.H2(heading) md.P(cleaned) } func renderQuestSection(md *render.Builder, page *wiki.ParsedPage, client *wiki.Client, title string, heading string, sectionNames []string) { for _, s := range page.Sections { lower := strings.ToLower(s.Line) for _, target := range sectionNames { if lower == target || strings.Contains(lower, target) { idx := 0 _, _ = fmt.Sscanf(s.Index, "%d", &idx) if idx > 0 { sectionPage, err := client.GetPageSection(title, idx) if err == nil { plain := extract.ExtractPlainText(sectionPage.Wikitext) if strings.TrimSpace(plain) != "" { md.H2(heading) md.P(plain) } } } return } } } } func init() { RegisterCommand(newQuestCmd) }