From 085e728d22a3369bd110b77409914cfbe9ebe611 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Tue, 16 Jun 2026 04:17:43 -0400 Subject: feat: recipe system, combining items, cooking skill --- internal/action/recipe.go | 213 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 internal/action/recipe.go (limited to 'internal/action') diff --git a/internal/action/recipe.go b/internal/action/recipe.go new file mode 100644 index 0000000..7627047 --- /dev/null +++ b/internal/action/recipe.go @@ -0,0 +1,213 @@ +package action + +import ( + "fmt" + "os" + "path/filepath" + + "gopkg.in/yaml.v3" +) + +type ConsumeEntry struct { + Items []string `yaml:"items"` + Qty int `yaml:"qty"` +} + +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"` + Consume []ConsumeEntry `yaml:"consume"` + Output string `yaml:"output"` + 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) 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 (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) 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 +} -- cgit v1.2.3