295 lines
6.0 KiB
Go

package items
import (
"dk/internal/store"
"fmt"
"sort"
"sync"
)
// Item represents an item in the game
type Item struct {
ID int `json:"id"`
Type int `json:"type"`
Name string `json:"name"`
Value int `json:"value"`
Att int `json:"att"`
Special string `json:"special"`
}
func (i *Item) Save() error {
itemStore := GetStore()
itemStore.UpdateItem(i)
return nil
}
func (i *Item) Delete() error {
itemStore := GetStore()
itemStore.RemoveItem(i.ID)
return nil
}
// Creates a new Item with sensible defaults
func New() *Item {
return &Item{
Type: TypeWeapon, // Default to weapon
Name: "",
Value: 0,
Att: 0,
Special: "",
}
}
// Validate checks if item has valid values
func (i *Item) Validate() error {
if i.Name == "" {
return fmt.Errorf("item name cannot be empty")
}
if i.Type < TypeWeapon || i.Type > TypeShield {
return fmt.Errorf("invalid item type: %d", i.Type)
}
if i.Value < 0 {
return fmt.Errorf("item Value cannot be negative")
}
if i.Att < 0 {
return fmt.Errorf("item Att cannot be negative")
}
return nil
}
// ItemType constants for item types
const (
TypeWeapon = 1
TypeArmor = 2
TypeShield = 3
)
// ItemStore provides in-memory storage with O(1) lookups and item-specific indices
type ItemStore struct {
*store.BaseStore[Item] // Embedded generic store
byType map[int][]int // Type -> []ID
allByID []int // All IDs sorted by ID
mu sync.RWMutex // Protects indices
}
// Global in-memory store
var itemStore *ItemStore
var storeOnce sync.Once
// Initialize the in-memory store
func initStore() {
itemStore = &ItemStore{
BaseStore: store.NewBaseStore[Item](),
byType: make(map[int][]int),
allByID: make([]int, 0),
}
}
// GetStore returns the global item store
func GetStore() *ItemStore {
storeOnce.Do(initStore)
return itemStore
}
// AddItem adds an item to the in-memory store and updates all indices
func (is *ItemStore) AddItem(item *Item) {
is.mu.Lock()
defer is.mu.Unlock()
// Validate item
if err := item.Validate(); err != nil {
return
}
// Add to base store
is.Add(item.ID, item)
// Rebuild indices
is.rebuildIndicesUnsafe()
}
// RemoveItem removes an item from the store and updates indices
func (is *ItemStore) RemoveItem(id int) {
is.mu.Lock()
defer is.mu.Unlock()
// Remove from base store
is.Remove(id)
// Rebuild indices
is.rebuildIndicesUnsafe()
}
// UpdateItem updates an item efficiently
func (is *ItemStore) UpdateItem(item *Item) {
is.mu.Lock()
defer is.mu.Unlock()
// Validate item
if err := item.Validate(); err != nil {
return
}
// Update base store
is.Add(item.ID, item)
// Rebuild indices
is.rebuildIndicesUnsafe()
}
// LoadData loads item data from JSON file, or starts with empty store
func LoadData(dataPath string) error {
is := GetStore()
// Load from base store, which handles JSON loading
if err := is.BaseStore.LoadData(dataPath); err != nil {
return err
}
// Rebuild indices from loaded data
is.rebuildIndices()
return nil
}
// SaveData saves item data to JSON file
func SaveData(dataPath string) error {
is := GetStore()
return is.BaseStore.SaveData(dataPath)
}
// rebuildIndicesUnsafe rebuilds all indices from base store data (caller must hold lock)
func (is *ItemStore) rebuildIndicesUnsafe() {
// Clear indices
is.byType = make(map[int][]int)
is.allByID = make([]int, 0)
// Collect all items and build indices
allItems := is.GetAll()
for id, item := range allItems {
// Type index
is.byType[item.Type] = append(is.byType[item.Type], id)
// All IDs
is.allByID = append(is.allByID, id)
}
// Sort allByID by ID
sort.Ints(is.allByID)
// Sort type indices by ID
for itemType := range is.byType {
sort.Ints(is.byType[itemType])
}
}
// rebuildIndices rebuilds all item-specific indices from base store data
func (is *ItemStore) rebuildIndices() {
is.mu.Lock()
defer is.mu.Unlock()
is.rebuildIndicesUnsafe()
}
// Retrieves an item by ID
func Find(id int) (*Item, error) {
is := GetStore()
item, exists := is.GetByID(id)
if !exists {
return nil, fmt.Errorf("item with ID %d not found", id)
}
return item, nil
}
// Retrieves all items
func All() ([]*Item, error) {
is := GetStore()
is.mu.RLock()
defer is.mu.RUnlock()
result := make([]*Item, 0, len(is.allByID))
for _, id := range is.allByID {
if item, exists := is.GetByID(id); exists {
result = append(result, item)
}
}
return result, nil
}
// Retrieves items by type
func ByType(itemType int) ([]*Item, error) {
is := GetStore()
is.mu.RLock()
defer is.mu.RUnlock()
ids, exists := is.byType[itemType]
if !exists {
return []*Item{}, nil
}
result := make([]*Item, 0, len(ids))
for _, id := range ids {
if item, exists := is.GetByID(id); exists {
result = append(result, item)
}
}
return result, nil
}
// Saves a new item to the in-memory store and sets the ID
func (i *Item) Insert() error {
is := GetStore()
// Validate before insertion
if err := i.Validate(); err != nil {
return fmt.Errorf("validation failed: %w", err)
}
// Assign new ID if not set
if i.ID == 0 {
i.ID = is.GetNextID()
}
// Add to store
is.AddItem(i)
return nil
}
// Returns true if the item is a weapon
func (i *Item) IsWeapon() bool {
return i.Type == TypeWeapon
}
// Returns true if the item is armor
func (i *Item) IsArmor() bool {
return i.Type == TypeArmor
}
// Returns true if the item is a shield
func (i *Item) IsShield() bool {
return i.Type == TypeShield
}
// Returns the string representation of the item type
func (i *Item) TypeName() string {
switch i.Type {
case TypeWeapon:
return "Weapon"
case TypeArmor:
return "Armor"
case TypeShield:
return "Shield"
default:
return "Unknown"
}
}
// Returns true if the item has special properties
func (i *Item) HasSpecial() bool {
return i.Special != ""
}
// Returns true if the item can be equipped
func (i *Item) IsEquippable() bool {
return i.Type == TypeWeapon || i.Type == TypeArmor || i.Type == TypeShield
}