272 lines
6.0 KiB
Go
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
|
|
}
|