aboutsummaryrefslogtreecommitdiff
path: root/internal/action
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-16 04:17:43 -0400
committerhistoria <[not public]>2026-06-16 04:17:43 -0400
commit085e728d22a3369bd110b77409914cfbe9ebe611 (patch)
treeeeb88cb1ceb91cc9ea2b5a1877c3c66328fab503 /internal/action
parent43f21aabae8924f35c12c8485e690aeefe0089fb (diff)
downloadthehouseoficarus-085e728d22a3369bd110b77409914cfbe9ebe611.tar.gz
feat: recipe system, combining items, cooking skill
Diffstat (limited to 'internal/action')
-rw-r--r--internal/action/recipe.go213
1 files changed, 213 insertions, 0 deletions
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
+}