package game import ( "sync" "thehouseoficarus/internal/item" ) type CraftIndex struct { mu sync.RWMutex byType map[string][]*item.ItemDef bySubtype map[string][]*item.ItemDef byInput map[string][]*item.ItemDef byStation map[string][]*item.ItemDef } func NewCraftIndex() *CraftIndex { return &CraftIndex{ byType: make(map[string][]*item.ItemDef), bySubtype: make(map[string][]*item.ItemDef), byInput: make(map[string][]*item.ItemDef), byStation: make(map[string][]*item.ItemDef), } } func (idx *CraftIndex) Build(items []*item.ItemDef) { idx.mu.Lock() defer idx.mu.Unlock() idx.byType = make(map[string][]*item.ItemDef) idx.bySubtype = make(map[string][]*item.ItemDef) idx.byInput = make(map[string][]*item.ItemDef) idx.byStation = make(map[string][]*item.ItemDef) for _, def := range items { if len(def.Craft) == 0 { continue } for ci := range def.Craft { craft := &def.Craft[ci] t := craft.Type if t == "" { t = "combine" } idx.byType[t] = appendUnique(idx.byType[t], def) if craft.Subtype != "" { idx.bySubtype[craft.Subtype] = appendUnique(idx.bySubtype[craft.Subtype], def) } for _, e := range craft.Ingredients { for _, id := range e.Items { idx.byInput[id] = appendUnique(idx.byInput[id], def) } } for _, sid := range craft.Station { idx.byStation[sid] = appendUnique(idx.byStation[sid], def) } } } } func appendUnique(items []*item.ItemDef, item *item.ItemDef) []*item.ItemDef { for _, existing := range items { if existing == item { return items } } return append(items, item) } func (idx *CraftIndex) ByType(craftType string) []*item.ItemDef { idx.mu.RLock() defer idx.mu.RUnlock() items := idx.byType[craftType] out := make([]*item.ItemDef, len(items)) copy(out, items) return out } func (idx *CraftIndex) BySubtype(subtype string) []*item.ItemDef { idx.mu.RLock() defer idx.mu.RUnlock() items := idx.bySubtype[subtype] out := make([]*item.ItemDef, len(items)) copy(out, items) return out } func (idx *CraftIndex) ByStation(stationDefID string) []*item.ItemDef { idx.mu.RLock() defer idx.mu.RUnlock() items := idx.byStation[stationDefID] out := make([]*item.ItemDef, len(items)) copy(out, items) return out } func (idx *CraftIndex) ByInput(itemID string) []*item.ItemDef { idx.mu.RLock() defer idx.mu.RUnlock() items := idx.byInput[itemID] out := make([]*item.ItemDef, len(items)) copy(out, items) return out } func (idx *CraftIndex) FindByTwoInputs(itemA, itemB string) []*item.ItemDef { idx.mu.RLock() defer idx.mu.RUnlock() candidatesA := idx.byInput[itemA] if len(candidatesA) == 0 { return nil } candidateSetB := make(map[string]bool) for _, def := range idx.byInput[itemB] { candidateSetB[def.ID] = true } var results []*item.ItemDef for _, def := range candidatesA { if !candidateSetB[def.ID] { continue } for _, craft := range def.Craft { entryA := craftEntry(craft, itemA) entryB := craftEntry(craft, itemB) if entryA >= 0 && entryB >= 0 && entryA != entryB { results = append(results, def) break } } } return results } func (idx *CraftIndex) FindByStationInput(stationDefID, itemID string) []*item.ItemDef { idx.mu.RLock() defer idx.mu.RUnlock() var results []*item.ItemDef for _, def := range idx.byStation[stationDefID] { for _, craft := range def.Craft { if craftMatchesEntry(&craft, itemID) { results = append(results, def) break } } } return results } func craftEntryFor(def *item.ItemDef, itemID string) int { for _, craft := range def.Craft { if ei := craftEntry(craft, itemID); ei >= 0 { return ei } } return -1 } func craftEntry(craft item.CraftDef, itemID string) int { for ei, e := range craft.Ingredients { for _, id := range e.Items { if id == itemID { return ei } } } return -1 } func craftMatchesEntry(c *item.CraftDef, itemID string) bool { if c == nil { return false } for _, e := range c.Ingredients { for _, id := range e.Items { if id == itemID { return true } } } return false } func craftHasAllItems(c *item.CraftDef, hasItem func(string) bool) bool { if c == nil { return false } for _, e := range c.Ingredients { found := false for _, id := range e.Items { if hasItem(id) { found = true break } } if !found { return false } } return true } func craftHasAllItemsQty(c *item.CraftDef, countItem func(string) int) bool { if c == nil { return false } for _, e := range c.Ingredients { found := false for _, id := range e.Items { qty := e.Quantity if qty <= 0 { qty = 1 } if countItem(id) >= qty { found = true break } } if !found { return false } } return true } func consumeIngredients(c *item.CraftDef, hasItem func(string) bool, removeItem func(string, int) bool) bool { if c == nil { return false } for _, e := range c.Ingredients { found := false for _, id := range e.Items { if hasItem(id) { removeItem(id, e.Quantity) found = true break } } if !found { return false } } return true }