223 lines
4.9 KiB
Go
223 lines
4.9 KiB
Go
package items
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"dk/internal/database"
|
|
"dk/internal/helpers/scanner"
|
|
|
|
"zombiezen.com/go/sqlite"
|
|
)
|
|
|
|
// Item represents an item in the database
|
|
type Item struct {
|
|
ID int `db:"id" json:"id"`
|
|
Type int `db:"type" json:"type"`
|
|
Name string `db:"name" json:"name"`
|
|
Value int `db:"value" json:"value"`
|
|
Att int `db:"att" json:"att"`
|
|
Special string `db:"special" json:"special"`
|
|
}
|
|
|
|
// New creates a new Item with sensible defaults
|
|
func New() *Item {
|
|
return &Item{
|
|
Type: TypeWeapon, // Default to weapon
|
|
Name: "",
|
|
Value: 0,
|
|
Att: 0,
|
|
Special: "",
|
|
}
|
|
}
|
|
|
|
var itemScanner = scanner.New[Item]()
|
|
|
|
// itemColumns returns the column list for item queries
|
|
func itemColumns() string {
|
|
return itemScanner.Columns()
|
|
}
|
|
|
|
// scanItem populates an Item struct using the fast scanner
|
|
func scanItem(stmt *sqlite.Stmt) *Item {
|
|
item := &Item{}
|
|
itemScanner.Scan(stmt, item)
|
|
return item
|
|
}
|
|
|
|
// ItemType constants for item types
|
|
const (
|
|
TypeWeapon = 1
|
|
TypeArmor = 2
|
|
TypeShield = 3
|
|
)
|
|
|
|
// Find retrieves an item by ID
|
|
func Find(id int) (*Item, error) {
|
|
var item *Item
|
|
|
|
query := `SELECT ` + itemColumns() + ` FROM items WHERE id = ?`
|
|
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
item = scanItem(stmt)
|
|
return nil
|
|
}, id)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find item: %w", err)
|
|
}
|
|
|
|
if item == nil {
|
|
return nil, fmt.Errorf("item with ID %d not found", id)
|
|
}
|
|
|
|
return item, nil
|
|
}
|
|
|
|
// All retrieves all items
|
|
func All() ([]*Item, error) {
|
|
var items []*Item
|
|
|
|
query := `SELECT ` + itemColumns() + ` FROM items ORDER BY id`
|
|
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
item := scanItem(stmt)
|
|
items = append(items, item)
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve all items: %w", err)
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
// ByType retrieves items by type
|
|
func ByType(itemType int) ([]*Item, error) {
|
|
var items []*Item
|
|
|
|
query := `SELECT ` + itemColumns() + ` FROM items WHERE type = ? ORDER BY id`
|
|
|
|
err := database.Query(query, func(stmt *sqlite.Stmt) error {
|
|
item := scanItem(stmt)
|
|
items = append(items, item)
|
|
return nil
|
|
}, itemType)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve items by type: %w", err)
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
// Save updates an existing item in the database
|
|
func (i *Item) Save() error {
|
|
if i.ID == 0 {
|
|
return fmt.Errorf("cannot save item without ID")
|
|
}
|
|
|
|
query := `UPDATE items SET type = ?, name = ?, value = ?, att = ?, special = ? WHERE id = ?`
|
|
return database.Exec(query, i.Type, i.Name, i.Value, i.Att, i.Special, i.ID)
|
|
}
|
|
|
|
// Insert saves a new item to the database and sets the ID
|
|
func (i *Item) Insert() error {
|
|
if i.ID != 0 {
|
|
return fmt.Errorf("item already has ID %d, use Save() to update", i.ID)
|
|
}
|
|
|
|
// Use a transaction to ensure we can get the ID
|
|
err := database.Transaction(func(tx *database.Tx) error {
|
|
query := `INSERT INTO items (type, name, value, att, special) VALUES (?, ?, ?, ?, ?)`
|
|
|
|
if err := tx.Exec(query, i.Type, i.Name, i.Value, i.Att, i.Special); err != nil {
|
|
return fmt.Errorf("failed to insert item: %w", err)
|
|
}
|
|
|
|
// Get the last insert ID
|
|
var id int
|
|
err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error {
|
|
id = stmt.ColumnInt(0)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get insert ID: %w", err)
|
|
}
|
|
|
|
i.ID = id
|
|
return nil
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
// Delete removes the item from the database
|
|
func (i *Item) Delete() error {
|
|
if i.ID == 0 {
|
|
return fmt.Errorf("cannot delete item without ID")
|
|
}
|
|
|
|
query := "DELETE FROM items WHERE id = ?"
|
|
return database.Exec(query, i.ID)
|
|
}
|
|
|
|
// IsWeapon returns true if the item is a weapon
|
|
func (i *Item) IsWeapon() bool {
|
|
return i.Type == TypeWeapon
|
|
}
|
|
|
|
// IsArmor returns true if the item is armor
|
|
func (i *Item) IsArmor() bool {
|
|
return i.Type == TypeArmor
|
|
}
|
|
|
|
// IsShield returns true if the item is a shield
|
|
func (i *Item) IsShield() bool {
|
|
return i.Type == TypeShield
|
|
}
|
|
|
|
// TypeName returns the string representation of the item type
|
|
func (i *Item) TypeName() string {
|
|
switch i.Type {
|
|
case TypeWeapon:
|
|
return "Weapon"
|
|
case TypeArmor:
|
|
return "Armor"
|
|
case TypeShield:
|
|
return "Shield"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
// HasSpecial returns true if the item has special properties
|
|
func (i *Item) HasSpecial() bool {
|
|
return i.Special != ""
|
|
}
|
|
|
|
// IsEquippable returns true if the item can be equipped
|
|
func (i *Item) IsEquippable() bool {
|
|
return i.Type == TypeWeapon || i.Type == TypeArmor || i.Type == TypeShield
|
|
}
|
|
|
|
// ToMap converts the item to a map for efficient template rendering
|
|
func (i *Item) ToMap() map[string]any {
|
|
return map[string]any{
|
|
"ID": i.ID,
|
|
"Type": i.Type,
|
|
"Name": i.Name,
|
|
"Value": i.Value,
|
|
"Att": i.Att,
|
|
"Special": i.Special,
|
|
|
|
// Computed values
|
|
"IsWeapon": i.IsWeapon(),
|
|
"IsArmor": i.IsArmor(),
|
|
"IsShield": i.IsShield(),
|
|
"TypeName": i.TypeName(),
|
|
"HasSpecial": i.HasSpecial(),
|
|
"IsEquippable": i.IsEquippable(),
|
|
}
|
|
}
|