package drops import ( "dk/internal/store" "fmt" "sort" "sync" ) // Drop represents a drop item in the game type Drop struct { ID int `json:"id"` Name string `json:"name"` Level int `json:"level"` Type int `json:"type"` Att string `json:"att"` } func (d *Drop) Save() error { dropStore := GetStore() dropStore.UpdateDrop(d) return nil } func (d *Drop) Delete() error { dropStore := GetStore() dropStore.RemoveDrop(d.ID) return nil } // Creates a new Drop with sensible defaults func New() *Drop { return &Drop{ Name: "", Level: 1, // Default minimum level Type: TypeConsumable, // Default to consumable Att: "", } } // Validate checks if drop has valid values func (d *Drop) Validate() error { if d.Name == "" { return fmt.Errorf("drop name cannot be empty") } if d.Level < 1 { return fmt.Errorf("drop Level must be at least 1") } if d.Type < TypeConsumable { return fmt.Errorf("invalid drop type: %d", d.Type) } return nil } // DropType constants for drop types const ( TypeConsumable = 1 ) // DropStore provides in-memory storage with O(1) lookups and drop-specific indices type DropStore struct { *store.BaseStore[Drop] // Embedded generic store byLevel map[int][]int // Level -> []ID byType map[int][]int // Type -> []ID allByID []int // All IDs sorted by ID mu sync.RWMutex // Protects indices } // Global in-memory store var dropStore *DropStore var storeOnce sync.Once // Initialize the in-memory store func initStore() { dropStore = &DropStore{ BaseStore: store.NewBaseStore[Drop](), byLevel: make(map[int][]int), byType: make(map[int][]int), allByID: make([]int, 0), } } // GetStore returns the global drop store func GetStore() *DropStore { storeOnce.Do(initStore) return dropStore } // AddDrop adds a drop to the in-memory store and updates all indices func (ds *DropStore) AddDrop(drop *Drop) { ds.mu.Lock() defer ds.mu.Unlock() // Validate drop if err := drop.Validate(); err != nil { return } // Add to base store ds.Add(drop.ID, drop) // Rebuild indices ds.rebuildIndicesUnsafe() } // RemoveDrop removes a drop from the store and updates indices func (ds *DropStore) RemoveDrop(id int) { ds.mu.Lock() defer ds.mu.Unlock() // Remove from base store ds.Remove(id) // Rebuild indices ds.rebuildIndicesUnsafe() } // UpdateDrop updates a drop efficiently func (ds *DropStore) UpdateDrop(drop *Drop) { ds.mu.Lock() defer ds.mu.Unlock() // Validate drop if err := drop.Validate(); err != nil { return } // Update base store ds.Add(drop.ID, drop) // Rebuild indices ds.rebuildIndicesUnsafe() } // LoadData loads drop data from JSON file, or starts with empty store func LoadData(dataPath string) error { ds := GetStore() // Load from base store, which handles JSON loading if err := ds.BaseStore.LoadData(dataPath); err != nil { return err } // Rebuild indices from loaded data ds.rebuildIndices() return nil } // SaveData saves drop data to JSON file func SaveData(dataPath string) error { ds := GetStore() return ds.BaseStore.SaveData(dataPath) } // rebuildIndicesUnsafe rebuilds all indices from base store data (caller must hold lock) func (ds *DropStore) rebuildIndicesUnsafe() { // Clear indices ds.byLevel = make(map[int][]int) ds.byType = make(map[int][]int) ds.allByID = make([]int, 0) // Collect all drops and build indices allDrops := ds.GetAll() for id, drop := range allDrops { // Level index ds.byLevel[drop.Level] = append(ds.byLevel[drop.Level], id) // Type index ds.byType[drop.Type] = append(ds.byType[drop.Type], id) // All IDs ds.allByID = append(ds.allByID, id) } // Sort allByID by ID sort.Ints(ds.allByID) // Sort level indices by ID for level := range ds.byLevel { sort.Ints(ds.byLevel[level]) } // Sort type indices by level, then ID for dropType := range ds.byType { sort.Slice(ds.byType[dropType], func(i, j int) bool { dropI, _ := ds.GetByID(ds.byType[dropType][i]) dropJ, _ := ds.GetByID(ds.byType[dropType][j]) if dropI.Level != dropJ.Level { return dropI.Level < dropJ.Level } return ds.byType[dropType][i] < ds.byType[dropType][j] }) } } // rebuildIndices rebuilds all drop-specific indices from base store data func (ds *DropStore) rebuildIndices() { ds.mu.Lock() defer ds.mu.Unlock() ds.rebuildIndicesUnsafe() } // Retrieves a drop by ID func Find(id int) (*Drop, error) { ds := GetStore() drop, exists := ds.GetByID(id) if !exists { return nil, fmt.Errorf("drop with ID %d not found", id) } return drop, nil } // Retrieves all drops func All() ([]*Drop, error) { ds := GetStore() ds.mu.RLock() defer ds.mu.RUnlock() result := make([]*Drop, 0, len(ds.allByID)) for _, id := range ds.allByID { if drop, exists := ds.GetByID(id); exists { result = append(result, drop) } } return result, nil } // Retrieves drops by minimum level requirement func ByLevel(minLevel int) ([]*Drop, error) { ds := GetStore() ds.mu.RLock() defer ds.mu.RUnlock() var result []*Drop for level := 1; level <= minLevel; level++ { if ids, exists := ds.byLevel[level]; exists { for _, id := range ids { if drop, exists := ds.GetByID(id); exists { result = append(result, drop) } } } } return result, nil } // Retrieves drops by type func ByType(dropType int) ([]*Drop, error) { ds := GetStore() ds.mu.RLock() defer ds.mu.RUnlock() ids, exists := ds.byType[dropType] if !exists { return []*Drop{}, nil } result := make([]*Drop, 0, len(ids)) for _, id := range ids { if drop, exists := ds.GetByID(id); exists { result = append(result, drop) } } return result, nil } // Saves a new drop to the in-memory store and sets the ID func (d *Drop) Insert() error { ds := GetStore() // Validate before insertion if err := d.Validate(); err != nil { return fmt.Errorf("validation failed: %w", err) } // Assign new ID if not set if d.ID == 0 { d.ID = ds.GetNextID() } // Add to store ds.AddDrop(d) return nil } // Returns true if the drop is a consumable item func (d *Drop) IsConsumable() bool { return d.Type == TypeConsumable } // Returns the string representation of the drop type func (d *Drop) TypeName() string { switch d.Type { case TypeConsumable: return "Consumable" default: return "Unknown" } }