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(), } }