272 lines
6.0 KiB
Go

package spells
import (
"fmt"
"dk/internal/database"
"dk/internal/helpers/scanner"
"zombiezen.com/go/sqlite"
)
// Spell represents a spell in the database
type Spell struct {
database.BaseModel
ID int `db:"id" json:"id"`
Name string `db:"name" json:"name"`
MP int `db:"mp" json:"mp"`
Attribute int `db:"attribute" json:"attribute"`
Type int `db:"type" json:"type"`
}
func (s *Spell) GetTableName() string {
return "spells"
}
func (s *Spell) GetID() int {
return s.ID
}
func (s *Spell) SetID(id int) {
s.ID = id
}
func (s *Spell) Set(field string, value any) error {
return database.Set(s, field, value)
}
func (s *Spell) Save() error {
return database.Save(s)
}
func (s *Spell) Delete() error {
return database.Delete(s)
}
// Creates a new Spell with sensible defaults
func New() *Spell {
return &Spell{
Name: "",
MP: 5, // Default MP cost
Attribute: 10, // Default attribute value
Type: TypeHealing, // Default to healing spell
}
}
var spellScanner = scanner.New[Spell]()
// Returns the column list for spell queries
func spellColumns() string {
return spellScanner.Columns()
}
// Populates a Spell struct using the fast scanner
func scanSpell(stmt *sqlite.Stmt) *Spell {
spell := &Spell{}
spellScanner.Scan(stmt, spell)
return spell
}
// SpellType constants for spell types
const (
TypeHealing = 1
TypeHurt = 2
TypeSleep = 3
TypeAttackBoost = 4
TypeDefenseBoost = 5
)
// Retrieves a spell by ID
func Find(id int) (*Spell, error) {
var spell *Spell
query := `SELECT ` + spellColumns() + ` FROM spells WHERE id = ?`
err := database.Query(query, func(stmt *sqlite.Stmt) error {
spell = scanSpell(stmt)
return nil
}, id)
if err != nil {
return nil, fmt.Errorf("failed to find spell: %w", err)
}
if spell == nil {
return nil, fmt.Errorf("spell with ID %d not found", id)
}
return spell, nil
}
// Retrieves all spells
func All() ([]*Spell, error) {
var spells []*Spell
query := `SELECT ` + spellColumns() + ` FROM spells ORDER BY type, mp, id`
err := database.Query(query, func(stmt *sqlite.Stmt) error {
spell := scanSpell(stmt)
spells = append(spells, spell)
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to retrieve all spells: %w", err)
}
return spells, nil
}
// Retrieves spells by type
func ByType(spellType int) ([]*Spell, error) {
var spells []*Spell
query := `SELECT ` + spellColumns() + ` FROM spells WHERE type = ? ORDER BY mp, id`
err := database.Query(query, func(stmt *sqlite.Stmt) error {
spell := scanSpell(stmt)
spells = append(spells, spell)
return nil
}, spellType)
if err != nil {
return nil, fmt.Errorf("failed to retrieve spells by type: %w", err)
}
return spells, nil
}
// Retrieves spells that cost at most the specified MP
func ByMaxMP(maxMP int) ([]*Spell, error) {
var spells []*Spell
query := `SELECT ` + spellColumns() + ` FROM spells WHERE mp <= ? ORDER BY type, mp, id`
err := database.Query(query, func(stmt *sqlite.Stmt) error {
spell := scanSpell(stmt)
spells = append(spells, spell)
return nil
}, maxMP)
if err != nil {
return nil, fmt.Errorf("failed to retrieve spells by max MP: %w", err)
}
return spells, nil
}
// Retrieves spells of a specific type that cost at most the specified MP
func ByTypeAndMaxMP(spellType, maxMP int) ([]*Spell, error) {
var spells []*Spell
query := `SELECT ` + spellColumns() + ` FROM spells WHERE type = ? AND mp <= ? ORDER BY mp, id`
err := database.Query(query, func(stmt *sqlite.Stmt) error {
spell := scanSpell(stmt)
spells = append(spells, spell)
return nil
}, spellType, maxMP)
if err != nil {
return nil, fmt.Errorf("failed to retrieve spells by type and max MP: %w", err)
}
return spells, nil
}
// Retrieves a spell by name (case-insensitive)
func ByName(name string) (*Spell, error) {
var spell *Spell
query := `SELECT ` + spellColumns() + ` FROM spells WHERE LOWER(name) = LOWER(?) LIMIT 1`
err := database.Query(query, func(stmt *sqlite.Stmt) error {
spell = scanSpell(stmt)
return nil
}, name)
if err != nil {
return nil, fmt.Errorf("failed to find spell by name: %w", err)
}
if spell == nil {
return nil, fmt.Errorf("spell with name '%s' not found", name)
}
return spell, nil
}
// Saves a new spell to the database and sets the ID
func (s *Spell) Insert() error {
columns := `name, mp, attribute, type`
values := []any{s.Name, s.MP, s.Attribute, s.Type}
return database.Insert(s, columns, values...)
}
// Returns true if the spell is a healing spell
func (s *Spell) IsHealing() bool {
return s.Type == TypeHealing
}
// Returns true if the spell is a hurt spell
func (s *Spell) IsHurt() bool {
return s.Type == TypeHurt
}
// Returns true if the spell is a sleep spell
func (s *Spell) IsSleep() bool {
return s.Type == TypeSleep
}
// Returns true if the spell boosts attack
func (s *Spell) IsAttackBoost() bool {
return s.Type == TypeAttackBoost
}
// Returns true if the spell boosts defense
func (s *Spell) IsDefenseBoost() bool {
return s.Type == TypeDefenseBoost
}
// Returns the string representation of the spell type
func (s *Spell) TypeName() string {
switch s.Type {
case TypeHealing:
return "Healing"
case TypeHurt:
return "Hurt"
case TypeSleep:
return "Sleep"
case TypeAttackBoost:
return "Attack Boost"
case TypeDefenseBoost:
return "Defense Boost"
default:
return "Unknown"
}
}
// Returns true if the spell can be cast with the given MP
func (s *Spell) CanCast(availableMP int) bool {
return availableMP >= s.MP
}
// Returns the attribute per MP ratio (higher is more efficient)
func (s *Spell) Efficiency() float64 {
if s.MP == 0 {
return 0
}
return float64(s.Attribute) / float64(s.MP)
}
// Returns true if the spell is used for attacking
func (s *Spell) IsOffensive() bool {
return s.Type == TypeHurt || s.Type == TypeSleep
}
// Returns true if the spell is used for support/buffs
func (s *Spell) IsSupport() bool {
return s.Type == TypeHealing || s.Type == TypeAttackBoost || s.Type == TypeDefenseBoost
}