aboutsummaryrefslogtreecommitdiff
path: root/internal/game/production_index.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-29 18:13:33 -0400
committerhistoria <[not public]>2026-06-29 18:13:33 -0400
commit7d76673fa0707d204c7d084c8f90c8133c22a31a (patch)
tree6fc832b9f3fde6ed455abb535f6e28b0e5db1fd9 /internal/game/production_index.go
parentfeb80b7dde200d4121cd9f9c583a08f9869c5588 (diff)
downloadthehouseoficarus-7d76673fa0707d204c7d084c8f90c8133c22a31a.tar.gz
feat: migrated map (and bfs) to full 3D instead of individual 2D maps on different planes.
Diffstat (limited to 'internal/game/production_index.go')
-rw-r--r--internal/game/production_index.go234
1 files changed, 234 insertions, 0 deletions
diff --git a/internal/game/production_index.go b/internal/game/production_index.go
new file mode 100644
index 0000000..648ccc8
--- /dev/null
+++ b/internal/game/production_index.go
@@ -0,0 +1,234 @@
+package game
+
+import (
+ "sync"
+
+ "thehouseoficarus/internal/item"
+)
+
+type CraftIndex struct {
+ mu sync.RWMutex
+ byType 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),
+ 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.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)
+
+ for _, e := range craft.Consume {
+ 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) 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.Consume {
+ 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.Consume {
+ 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.Consume {
+ 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.Consume {
+ 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 craftConsumeAll(c *item.CraftDef, hasItem func(string) bool, removeItem func(string, int) bool) bool {
+ if c == nil {
+ return false
+ }
+ for _, e := range c.Consume {
+ found := false
+ for _, id := range e.Items {
+ if hasItem(id) {
+ removeItem(id, e.Quantity)
+ found = true
+ break
+ }
+ }
+ if !found {
+ return false
+ }
+ }
+ return true
+}