package monsters import ( "fmt" "dk/internal/database" "dk/internal/helpers/scanner" "zombiezen.com/go/sqlite" ) // Monster represents a monster in the database type Monster struct { database.BaseModel ID int `db:"id" json:"id"` Name string `db:"name" json:"name"` MaxHP int `db:"max_hp" json:"max_hp"` MaxDmg int `db:"max_dmg" json:"max_dmg"` Armor int `db:"armor" json:"armor"` Level int `db:"level" json:"level"` MaxExp int `db:"max_exp" json:"max_exp"` MaxGold int `db:"max_gold" json:"max_gold"` Immune int `db:"immune" json:"immune"` } func (m *Monster) GetTableName() string { return "monsters" } func (m *Monster) GetID() int { return m.ID } func (m *Monster) SetID(id int) { m.ID = id } func (m *Monster) Set(field string, value any) error { return database.Set(m, field, value) } func (m *Monster) Save() error { return database.Save(m) } func (m *Monster) Delete() error { return database.Delete(m) } // Creates a new Monster with sensible defaults func New() *Monster { return &Monster{ Name: "", MaxHP: 10, // Default HP MaxDmg: 5, // Default damage Armor: 0, // Default armor Level: 1, // Default level MaxExp: 10, // Default exp reward MaxGold: 5, // Default gold reward Immune: ImmuneNone, // No immunity by default } } var monsterScanner = scanner.New[Monster]() // Returns the column list for monster queries func monsterColumns() string { return monsterScanner.Columns() } // Populates a Monster struct using the fast scanner func scanMonster(stmt *sqlite.Stmt) *Monster { monster := &Monster{} monsterScanner.Scan(stmt, monster) return monster } // Immunity constants for monster immunity types const ( ImmuneNone = 0 ImmuneHurt = 1 // Immune to Hurt spells ImmuneSleep = 2 // Immune to Sleep spells ) // Retrieves a monster by ID func Find(id int) (*Monster, error) { var monster *Monster query := `SELECT ` + monsterColumns() + ` FROM monsters WHERE id = ?` err := database.Query(query, func(stmt *sqlite.Stmt) error { monster = scanMonster(stmt) return nil }, id) if err != nil { return nil, fmt.Errorf("failed to find monster: %w", err) } if monster == nil { return nil, fmt.Errorf("monster with ID %d not found", id) } return monster, nil } // Retrieves all monsters func All() ([]*Monster, error) { var monsters []*Monster query := `SELECT ` + monsterColumns() + ` FROM monsters ORDER BY level, id` err := database.Query(query, func(stmt *sqlite.Stmt) error { monster := scanMonster(stmt) monsters = append(monsters, monster) return nil }) if err != nil { return nil, fmt.Errorf("failed to retrieve all monsters: %w", err) } return monsters, nil } // Retrieves monsters by level func ByLevel(level int) ([]*Monster, error) { var monsters []*Monster query := `SELECT ` + monsterColumns() + ` FROM monsters WHERE level = ? ORDER BY id` err := database.Query(query, func(stmt *sqlite.Stmt) error { monster := scanMonster(stmt) monsters = append(monsters, monster) return nil }, level) if err != nil { return nil, fmt.Errorf("failed to retrieve monsters by level: %w", err) } return monsters, nil } // Retrieves monsters within a level range (inclusive) func ByLevelRange(minLevel, maxLevel int) ([]*Monster, error) { var monsters []*Monster query := `SELECT ` + monsterColumns() + ` FROM monsters WHERE level BETWEEN ? AND ? ORDER BY level, id` err := database.Query(query, func(stmt *sqlite.Stmt) error { monster := scanMonster(stmt) monsters = append(monsters, monster) return nil }, minLevel, maxLevel) if err != nil { return nil, fmt.Errorf("failed to retrieve monsters by level range: %w", err) } return monsters, nil } // Retrieves monsters by immunity type func ByImmunity(immunityType int) ([]*Monster, error) { var monsters []*Monster query := `SELECT ` + monsterColumns() + ` FROM monsters WHERE immune = ? ORDER BY level, id` err := database.Query(query, func(stmt *sqlite.Stmt) error { monster := scanMonster(stmt) monsters = append(monsters, monster) return nil }, immunityType) if err != nil { return nil, fmt.Errorf("failed to retrieve monsters by immunity: %w", err) } return monsters, nil } // Saves a new monster to the database and sets the ID func (m *Monster) Insert() error { columns := `name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune` values := []any{m.Name, m.MaxHP, m.MaxDmg, m.Armor, m.Level, m.MaxExp, m.MaxGold, m.Immune} return database.Insert(m, columns, values...) } // Returns true if the monster is immune to Hurt spells func (m *Monster) IsHurtImmune() bool { return m.Immune == ImmuneHurt } // Returns true if the monster is immune to Sleep spells func (m *Monster) IsSleepImmune() bool { return m.Immune == ImmuneSleep } // Returns true if the monster has any immunity func (m *Monster) HasImmunity() bool { return m.Immune != ImmuneNone } // Returns the string representation of the monster's immunity 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" } } // Calculates a simple difficulty rating based on stats func (m *Monster) DifficultyRating() float64 { // Simple formula: (HP + Damage + Armor) / Level // Higher values indicate tougher monsters relative to their level if m.Level == 0 { return 0 } return float64(m.MaxHP+m.MaxDmg+m.Armor) / float64(m.Level) } // Returns the experience reward per hit point (efficiency metric) func (m *Monster) ExpPerHP() float64 { if m.MaxHP == 0 { return 0 } return float64(m.MaxExp) / float64(m.MaxHP) } // Returns the gold reward per hit point (efficiency metric) func (m *Monster) GoldPerHP() float64 { if m.MaxHP == 0 { return 0 } return float64(m.MaxGold) / float64(m.MaxHP) }