266 lines
6.5 KiB
Go

package spells
import (
"fmt"
"dk/internal/database"
"zombiezen.com/go/sqlite"
)
// Spell represents a spell in the database
type Spell struct {
ID int `json:"id"`
Name string `json:"name"`
MP int `json:"mp"`
Attribute int `json:"attribute"`
Type int `json:"type"`
db *database.DB
}
// SpellType constants for spell types
const (
TypeHealing = 1
TypeHurt = 2
TypeSleep = 3
TypeAttackBoost = 4
TypeDefenseBoost = 5
)
// Find retrieves a spell by ID
func Find(db *database.DB, id int) (*Spell, error) {
spell := &Spell{db: db}
query := "SELECT id, name, mp, attribute, type FROM spells WHERE id = ?"
err := db.Query(query, func(stmt *sqlite.Stmt) error {
spell.ID = stmt.ColumnInt(0)
spell.Name = stmt.ColumnText(1)
spell.MP = stmt.ColumnInt(2)
spell.Attribute = stmt.ColumnInt(3)
spell.Type = stmt.ColumnInt(4)
return nil
}, id)
if err != nil {
return nil, fmt.Errorf("failed to find spell: %w", err)
}
if spell.ID == 0 {
return nil, fmt.Errorf("spell with ID %d not found", id)
}
return spell, nil
}
// All retrieves all spells
func All(db *database.DB) ([]*Spell, error) {
var spells []*Spell
query := "SELECT id, name, mp, attribute, type FROM spells ORDER BY type, mp, id"
err := db.Query(query, func(stmt *sqlite.Stmt) error {
spell := &Spell{
ID: stmt.ColumnInt(0),
Name: stmt.ColumnText(1),
MP: stmt.ColumnInt(2),
Attribute: stmt.ColumnInt(3),
Type: stmt.ColumnInt(4),
db: db,
}
spells = append(spells, spell)
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to retrieve all spells: %w", err)
}
return spells, nil
}
// ByType retrieves spells by type
func ByType(db *database.DB, spellType int) ([]*Spell, error) {
var spells []*Spell
query := "SELECT id, name, mp, attribute, type FROM spells WHERE type = ? ORDER BY mp, id"
err := db.Query(query, func(stmt *sqlite.Stmt) error {
spell := &Spell{
ID: stmt.ColumnInt(0),
Name: stmt.ColumnText(1),
MP: stmt.ColumnInt(2),
Attribute: stmt.ColumnInt(3),
Type: stmt.ColumnInt(4),
db: db,
}
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
}
// ByMaxMP retrieves spells that cost at most the specified MP
func ByMaxMP(db *database.DB, maxMP int) ([]*Spell, error) {
var spells []*Spell
query := "SELECT id, name, mp, attribute, type FROM spells WHERE mp <= ? ORDER BY type, mp, id"
err := db.Query(query, func(stmt *sqlite.Stmt) error {
spell := &Spell{
ID: stmt.ColumnInt(0),
Name: stmt.ColumnText(1),
MP: stmt.ColumnInt(2),
Attribute: stmt.ColumnInt(3),
Type: stmt.ColumnInt(4),
db: db,
}
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
}
// ByTypeAndMaxMP retrieves spells of a specific type that cost at most the specified MP
func ByTypeAndMaxMP(db *database.DB, spellType, maxMP int) ([]*Spell, error) {
var spells []*Spell
query := "SELECT id, name, mp, attribute, type FROM spells WHERE type = ? AND mp <= ? ORDER BY mp, id"
err := db.Query(query, func(stmt *sqlite.Stmt) error {
spell := &Spell{
ID: stmt.ColumnInt(0),
Name: stmt.ColumnText(1),
MP: stmt.ColumnInt(2),
Attribute: stmt.ColumnInt(3),
Type: stmt.ColumnInt(4),
db: db,
}
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
}
// ByName retrieves a spell by name (case-insensitive)
func ByName(db *database.DB, name string) (*Spell, error) {
spell := &Spell{db: db}
query := "SELECT id, name, mp, attribute, type FROM spells WHERE LOWER(name) = LOWER(?) LIMIT 1"
err := db.Query(query, func(stmt *sqlite.Stmt) error {
spell.ID = stmt.ColumnInt(0)
spell.Name = stmt.ColumnText(1)
spell.MP = stmt.ColumnInt(2)
spell.Attribute = stmt.ColumnInt(3)
spell.Type = stmt.ColumnInt(4)
return nil
}, name)
if err != nil {
return nil, fmt.Errorf("failed to find spell by name: %w", err)
}
if spell.ID == 0 {
return nil, fmt.Errorf("spell with name '%s' not found", name)
}
return spell, nil
}
// Save updates an existing spell in the database
func (s *Spell) Save() error {
if s.ID == 0 {
return fmt.Errorf("cannot save spell without ID")
}
query := `UPDATE spells SET name = ?, mp = ?, attribute = ?, type = ? WHERE id = ?`
return s.db.Exec(query, s.Name, s.MP, s.Attribute, s.Type, s.ID)
}
// Delete removes the spell from the database
func (s *Spell) Delete() error {
if s.ID == 0 {
return fmt.Errorf("cannot delete spell without ID")
}
query := "DELETE FROM spells WHERE id = ?"
return s.db.Exec(query, s.ID)
}
// IsHealing returns true if the spell is a healing spell
func (s *Spell) IsHealing() bool {
return s.Type == TypeHealing
}
// IsHurt returns true if the spell is a hurt spell
func (s *Spell) IsHurt() bool {
return s.Type == TypeHurt
}
// IsSleep returns true if the spell is a sleep spell
func (s *Spell) IsSleep() bool {
return s.Type == TypeSleep
}
// IsAttackBoost returns true if the spell boosts attack
func (s *Spell) IsAttackBoost() bool {
return s.Type == TypeAttackBoost
}
// IsDefenseBoost returns true if the spell boosts defense
func (s *Spell) IsDefenseBoost() bool {
return s.Type == TypeDefenseBoost
}
// TypeName 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"
}
}
// CanCast returns true if the spell can be cast with the given MP
func (s *Spell) CanCast(availableMP int) bool {
return availableMP >= s.MP
}
// Efficiency 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)
}
// IsOffensive returns true if the spell is used for attacking
func (s *Spell) IsOffensive() bool {
return s.Type == TypeHurt || s.Type == TypeSleep
}
// IsSupport 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
}