package monsters import ( "dk/internal/store" "fmt" ) // Monster represents a monster in the game type Monster struct { ID int `json:"id"` Name string `json:"name"` MaxHP int `json:"max_hp"` MaxDmg int `json:"max_dmg"` Armor int `json:"armor"` Level int `json:"level"` MaxExp int `json:"max_exp"` MaxGold int `json:"max_gold"` Immune int `json:"immune"` } func (m *Monster) Save() error { return GetStore().UpdateWithRebuild(m.ID, m) } func (m *Monster) Delete() error { GetStore().RemoveWithRebuild(m.ID) return nil } // Creates a new Monster with sensible defaults func New() *Monster { return &Monster{ Name: "", MaxHP: 10, MaxDmg: 5, Armor: 0, Level: 1, MaxExp: 10, MaxGold: 5, Immune: ImmuneNone, } } // Validate checks if monster has valid values func (m *Monster) Validate() error { if m.Name == "" { return fmt.Errorf("monster name cannot be empty") } if m.MaxHP < 1 { return fmt.Errorf("monster MaxHP must be at least 1") } if m.Level < 1 { return fmt.Errorf("monster Level must be at least 1") } if m.Immune < ImmuneNone || m.Immune > ImmuneSleep { return fmt.Errorf("invalid immunity type: %d", m.Immune) } return nil } // Immunity constants const ( ImmuneNone = 0 ImmuneHurt = 1 ImmuneSleep = 2 ) // MonsterStore with enhanced BaseStore type MonsterStore struct { *store.BaseStore[Monster] } // Global store with singleton pattern var GetStore = store.NewSingleton(func() *MonsterStore { ms := &MonsterStore{BaseStore: store.NewBaseStore[Monster]()} // Register indices ms.RegisterIndex("byLevel", store.BuildIntGroupIndex(func(m *Monster) int { return m.Level })) ms.RegisterIndex("byImmunity", store.BuildIntGroupIndex(func(m *Monster) int { return m.Immune })) ms.RegisterIndex("allByLevel", store.BuildSortedListIndex(func(a, b *Monster) bool { if a.Level == b.Level { return a.ID < b.ID } return a.Level < b.Level })) return ms }) // Enhanced CRUD operations func (ms *MonsterStore) AddMonster(monster *Monster) error { return ms.AddWithRebuild(monster.ID, monster) } func (ms *MonsterStore) RemoveMonster(id int) { ms.RemoveWithRebuild(id) } func (ms *MonsterStore) UpdateMonster(monster *Monster) error { return ms.UpdateWithRebuild(monster.ID, monster) } // Data persistence func LoadData(dataPath string) error { ms := GetStore() return ms.BaseStore.LoadData(dataPath) } func SaveData(dataPath string) error { ms := GetStore() return ms.BaseStore.SaveData(dataPath) } // Query functions using enhanced store func Find(id int) (*Monster, error) { ms := GetStore() monster, exists := ms.Find(id) if !exists { return nil, fmt.Errorf("monster with ID %d not found", id) } return monster, nil } func All() ([]*Monster, error) { ms := GetStore() return ms.AllSorted("allByLevel"), nil } func ByLevel(level int) ([]*Monster, error) { ms := GetStore() return ms.GroupByIndex("byLevel", level), nil } func ByLevelRange(minLevel, maxLevel int) ([]*Monster, error) { ms := GetStore() var result []*Monster for level := minLevel; level <= maxLevel; level++ { monsters := ms.GroupByIndex("byLevel", level) result = append(result, monsters...) } return result, nil } func ByImmunity(immunityType int) ([]*Monster, error) { ms := GetStore() return ms.GroupByIndex("byImmunity", immunityType), nil } // Insert with ID assignment func (m *Monster) Insert() error { ms := GetStore() if m.ID == 0 { m.ID = ms.GetNextID() } return ms.AddMonster(m) } // Helper methods func (m *Monster) IsHurtImmune() bool { return m.Immune == ImmuneHurt } func (m *Monster) IsSleepImmune() bool { return m.Immune == ImmuneSleep } func (m *Monster) HasImmunity() bool { return m.Immune != ImmuneNone } func (m *Monster) ImmunityName() string { switch m.Immune { case ImmuneNone: return "None" case ImmuneHurt: return "Hurt Spells" case ImmuneSleep: return "Sleep Spells" default: return "Unknown" } } func (m *Monster) DifficultyRating() float64 { if m.Level == 0 { return 0 } return float64(m.MaxHP+m.MaxDmg+m.Armor) / float64(m.Level) } func (m *Monster) ExpPerHP() float64 { if m.MaxHP == 0 { return 0 } return float64(m.MaxExp) / float64(m.MaxHP) } func (m *Monster) GoldPerHP() float64 { if m.MaxHP == 0 { return 0 } return float64(m.MaxGold) / float64(m.MaxHP) }