package action import ( "fmt" "os" "path/filepath" "gopkg.in/yaml.v3" ) type ConsumeEntry struct { Items []string `yaml:"items"` Qty int `yaml:"qty"` Byproducts []string `yaml:"byproducts"` } type RecipeDef struct { ID string `yaml:"id"` Type string `yaml:"type"` Skill string `yaml:"skill"` Level int `yaml:"level"` XP int `yaml:"xp"` Station []string `yaml:"station"` Tool string `yaml:"tool"` Consume []ConsumeEntry `yaml:"consume"` Output string `yaml:"output"` OutputQty int `yaml:"output_qty"` Fail string `yaml:"fail"` Message string `yaml:"message"` FailMessage string `yaml:"fail_message"` Wait float64 `yaml:"wait"` Success *SuccessFormula `yaml:"success"` } type RecipeStore struct { dataDir string } func NewRecipeStore(dataDir string) *RecipeStore { return &RecipeStore{dataDir: dataDir} } func (s *RecipeStore) LoadAll() ([]RecipeDef, error) { dir := filepath.Join(s.dataDir, "recipes") entries, err := os.ReadDir(dir) if err != nil { return nil, err } var recipes []RecipeDef for _, e := range entries { if e.IsDir() || filepath.Ext(e.Name()) != ".yaml" { continue } data, err := os.ReadFile(filepath.Join(dir, e.Name())) if err != nil { continue } var r RecipeDef if err := yaml.Unmarshal(data, &r); err != nil { continue } recipes = append(recipes, r) } return recipes, nil } func (r *RecipeDef) MatchesEntry(itemID string) bool { for _, e := range r.Consume { for _, id := range e.Items { if id == itemID { return true } } } return false } func (s *RecipeStore) FindByInput(recipeType string, itemID string) *RecipeDef { all, err := s.LoadAll() if err != nil { return nil } for i := range all { if recipeType != "" && all[i].Type != recipeType { continue } for _, e := range all[i].Consume { for _, id := range e.Items { if id == itemID { return &all[i] } } } } return nil } func (s *RecipeStore) FindByItem(itemID, recipeType string) ([]RecipeDef, error) { all, err := s.LoadAll() if err != nil { return nil, err } var matches []RecipeDef for _, r := range all { if recipeType != "" && r.Type != recipeType { continue } if r.MatchesEntry(itemID) { matches = append(matches, r) } } return matches, nil } func (s *RecipeStore) FindByItems(a, b string) ([]RecipeDef, error) { all, err := s.LoadAll() if err != nil { return nil, err } var matches []RecipeDef for _, r := range all { if len(r.Consume) < 2 { continue } aEntry := -1 bEntry := -1 for ei, e := range r.Consume { for _, id := range e.Items { if id == a && aEntry < 0 { aEntry = ei } if id == b && bEntry < 0 { bEntry = ei } } } if aEntry >= 0 && bEntry >= 0 && aEntry != bEntry { matches = append(matches, r) } } return matches, nil } func (s *RecipeStore) FindByStation(itemID string, stationIDs []string, recipeType string) ([]RecipeDef, error) { all, err := s.LoadAll() if err != nil { return nil, err } var matches []RecipeDef for _, r := range all { if recipeType != "" && r.Type != recipeType { continue } if !r.MatchesEntry(itemID) { continue } if len(r.Station) == 0 { continue } for _, station := range stationIDs { for _, rs := range r.Station { if rs == station { matches = append(matches, r) goto next } } } next: } return matches, nil } func (r *RecipeDef) DisplayName() string { return r.Output } func (r *RecipeDef) EffectiveSkill() string { if r.Skill != "" { return r.Skill } return r.Type } func (s *RecipeStore) FindByNameOrID(query string, recipeType string) (*RecipeDef, error) { all, err := s.LoadAll() if err != nil { return nil, err } for _, r := range all { if recipeType != "" && r.Type != recipeType { continue } if r.ID == query || r.Output == query { return &r, nil } } return nil, fmt.Errorf("recipe not found: %s", query) } func (r *RecipeDef) FirstItemName(load func(string) (string, bool)) string { for _, e := range r.Consume { for _, id := range e.Items { if name, ok := load(id); ok { return name } return id } } return "" } func (r *RecipeDef) ConsumeAll(hasItem func(string) bool, removeItem func(string, int) bool) bool { for _, e := range r.Consume { found := false for _, id := range e.Items { if hasItem(id) { removeItem(id, e.Qty) found = true break } } if !found { return false } } return true } func (r *RecipeDef) HasAllItemsQty(countItem func(string) int) bool { for _, e := range r.Consume { found := false for _, id := range e.Items { qty := e.Qty if qty <= 0 { qty = 1 } if countItem(id) >= qty { found = true break } } if !found { return false } } return true } func (r *RecipeDef) HasAllItems(hasItem func(string) bool) bool { for _, e := range r.Consume { found := false for _, id := range e.Items { if hasItem(id) { found = true break } } if !found { return false } } return true }