241 lines
5.3 KiB
Go
241 lines
5.3 KiB
Go
package drops
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"dk/internal/database"
|
|
|
|
"zombiezen.com/go/sqlite"
|
|
)
|
|
|
|
// Drop represents a drop item in the database
|
|
type Drop struct {
|
|
ID int `json:"id"`
|
|
Name string `json:"name"`
|
|
Level int `json:"level"`
|
|
Type int `json:"type"`
|
|
Att string `json:"att"`
|
|
|
|
db *database.DB
|
|
}
|
|
|
|
// DropType constants for drop types
|
|
const (
|
|
TypeConsumable = 1
|
|
)
|
|
|
|
// Find retrieves a drop by ID
|
|
func Find(db *database.DB, id int) (*Drop, error) {
|
|
drop := &Drop{db: db}
|
|
|
|
query := "SELECT id, name, level, type, att FROM drops WHERE id = ?"
|
|
err := db.Query(query, func(stmt *sqlite.Stmt) error {
|
|
drop.ID = stmt.ColumnInt(0)
|
|
drop.Name = stmt.ColumnText(1)
|
|
drop.Level = stmt.ColumnInt(2)
|
|
drop.Type = stmt.ColumnInt(3)
|
|
drop.Att = stmt.ColumnText(4)
|
|
return nil
|
|
}, id)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find drop: %w", err)
|
|
}
|
|
|
|
if drop.ID == 0 {
|
|
return nil, fmt.Errorf("drop with ID %d not found", id)
|
|
}
|
|
|
|
return drop, nil
|
|
}
|
|
|
|
// All retrieves all drops
|
|
func All(db *database.DB) ([]*Drop, error) {
|
|
var drops []*Drop
|
|
|
|
query := "SELECT id, name, level, type, att FROM drops ORDER BY id"
|
|
err := db.Query(query, func(stmt *sqlite.Stmt) error {
|
|
drop := &Drop{
|
|
ID: stmt.ColumnInt(0),
|
|
Name: stmt.ColumnText(1),
|
|
Level: stmt.ColumnInt(2),
|
|
Type: stmt.ColumnInt(3),
|
|
Att: stmt.ColumnText(4),
|
|
db: db,
|
|
}
|
|
drops = append(drops, drop)
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve all drops: %w", err)
|
|
}
|
|
|
|
return drops, nil
|
|
}
|
|
|
|
// ByLevel retrieves drops by minimum level requirement
|
|
func ByLevel(db *database.DB, minLevel int) ([]*Drop, error) {
|
|
var drops []*Drop
|
|
|
|
query := "SELECT id, name, level, type, att FROM drops WHERE level <= ? ORDER BY level, id"
|
|
err := db.Query(query, func(stmt *sqlite.Stmt) error {
|
|
drop := &Drop{
|
|
ID: stmt.ColumnInt(0),
|
|
Name: stmt.ColumnText(1),
|
|
Level: stmt.ColumnInt(2),
|
|
Type: stmt.ColumnInt(3),
|
|
Att: stmt.ColumnText(4),
|
|
db: db,
|
|
}
|
|
drops = append(drops, drop)
|
|
return nil
|
|
}, minLevel)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve drops by level: %w", err)
|
|
}
|
|
|
|
return drops, nil
|
|
}
|
|
|
|
// ByType retrieves drops by type
|
|
func ByType(db *database.DB, dropType int) ([]*Drop, error) {
|
|
var drops []*Drop
|
|
|
|
query := "SELECT id, name, level, type, att FROM drops WHERE type = ? ORDER BY level, id"
|
|
err := db.Query(query, func(stmt *sqlite.Stmt) error {
|
|
drop := &Drop{
|
|
ID: stmt.ColumnInt(0),
|
|
Name: stmt.ColumnText(1),
|
|
Level: stmt.ColumnInt(2),
|
|
Type: stmt.ColumnInt(3),
|
|
Att: stmt.ColumnText(4),
|
|
db: db,
|
|
}
|
|
drops = append(drops, drop)
|
|
return nil
|
|
}, dropType)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve drops by type: %w", err)
|
|
}
|
|
|
|
return drops, nil
|
|
}
|
|
|
|
// Builder provides a fluent interface for creating drops
|
|
type Builder struct {
|
|
drop *Drop
|
|
db *database.DB
|
|
}
|
|
|
|
// NewBuilder creates a new drop builder
|
|
func NewBuilder(db *database.DB) *Builder {
|
|
return &Builder{
|
|
drop: &Drop{db: db},
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
// WithName sets the drop name
|
|
func (b *Builder) WithName(name string) *Builder {
|
|
b.drop.Name = name
|
|
return b
|
|
}
|
|
|
|
// WithLevel sets the drop level requirement
|
|
func (b *Builder) WithLevel(level int) *Builder {
|
|
b.drop.Level = level
|
|
return b
|
|
}
|
|
|
|
// WithType sets the drop type
|
|
func (b *Builder) WithType(dropType int) *Builder {
|
|
b.drop.Type = dropType
|
|
return b
|
|
}
|
|
|
|
// WithAtt sets the attributes
|
|
func (b *Builder) WithAtt(att string) *Builder {
|
|
b.drop.Att = att
|
|
return b
|
|
}
|
|
|
|
// Create saves the drop to the database and returns it
|
|
func (b *Builder) Create() (*Drop, error) {
|
|
// Use a transaction to ensure we can get the ID
|
|
var drop *Drop
|
|
err := b.db.Transaction(func(tx *database.Tx) error {
|
|
query := `INSERT INTO drops (name, level, type, att)
|
|
VALUES (?, ?, ?, ?)`
|
|
|
|
if err := tx.Exec(query, b.drop.Name, b.drop.Level, b.drop.Type, b.drop.Att); err != nil {
|
|
return fmt.Errorf("failed to insert drop: %w", err)
|
|
}
|
|
|
|
// Get the last inserted ID within the same transaction
|
|
var lastID int
|
|
err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error {
|
|
lastID = stmt.ColumnInt(0)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get last insert ID: %w", err)
|
|
}
|
|
|
|
// Create the drop with the ID
|
|
drop = &Drop{
|
|
ID: lastID,
|
|
Name: b.drop.Name,
|
|
Level: b.drop.Level,
|
|
Type: b.drop.Type,
|
|
Att: b.drop.Att,
|
|
db: b.db,
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create drop: %w", err)
|
|
}
|
|
|
|
return drop, nil
|
|
}
|
|
|
|
// Save updates an existing drop in the database
|
|
func (d *Drop) Save() error {
|
|
if d.ID == 0 {
|
|
return fmt.Errorf("cannot save drop without ID")
|
|
}
|
|
|
|
query := `UPDATE drops SET name = ?, level = ?, type = ?, att = ? WHERE id = ?`
|
|
return d.db.Exec(query, d.Name, d.Level, d.Type, d.Att, d.ID)
|
|
}
|
|
|
|
// Delete removes the drop from the database
|
|
func (d *Drop) Delete() error {
|
|
if d.ID == 0 {
|
|
return fmt.Errorf("cannot delete drop without ID")
|
|
}
|
|
|
|
query := "DELETE FROM drops WHERE id = ?"
|
|
return d.db.Exec(query, d.ID)
|
|
}
|
|
|
|
// IsConsumable returns true if the drop is a consumable item
|
|
func (d *Drop) IsConsumable() bool {
|
|
return d.Type == TypeConsumable
|
|
}
|
|
|
|
// TypeName returns the string representation of the drop type
|
|
func (d *Drop) TypeName() string {
|
|
switch d.Type {
|
|
case TypeConsumable:
|
|
return "Consumable"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|