266 lines
6.5 KiB
Go
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
|
|
}
|