316 lines
9.6 KiB
Go
316 lines
9.6 KiB
Go
package transmute
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"zombiezen.com/go/sqlite"
|
|
"zombiezen.com/go/sqlite/sqlitex"
|
|
)
|
|
|
|
// DatabaseImpl provides a default implementation of the Database interface
|
|
type DatabaseImpl struct {
|
|
conn *sqlite.Conn
|
|
}
|
|
|
|
// NewDatabase creates a new database implementation
|
|
func NewDatabase(conn *sqlite.Conn) *DatabaseImpl {
|
|
return &DatabaseImpl{conn: conn}
|
|
}
|
|
|
|
// OpenDB opens a database connection for transmutation system
|
|
func OpenDB(path string) (*DatabaseImpl, error) {
|
|
conn, err := sqlite.OpenConn(path, sqlite.OpenReadWrite|sqlite.OpenCreate|sqlite.OpenWAL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open database: %w", err)
|
|
}
|
|
|
|
// Enable foreign keys
|
|
if err := sqlitex.ExecTransient(conn, "PRAGMA foreign_keys = ON;", nil); err != nil {
|
|
conn.Close()
|
|
return nil, fmt.Errorf("failed to enable foreign keys: %w", err)
|
|
}
|
|
|
|
return &DatabaseImpl{conn: conn}, nil
|
|
}
|
|
|
|
// Close closes the database connection
|
|
func (dbi *DatabaseImpl) Close() error {
|
|
if dbi.conn != nil {
|
|
return dbi.conn.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LoadTransmutingTiers loads transmuting tiers from the database
|
|
func (dbi *DatabaseImpl) LoadTransmutingTiers() ([]*TransmutingTier, error) {
|
|
// Create transmuting_tiers table if it doesn't exist
|
|
if err := sqlitex.ExecuteScript(dbi.conn, `
|
|
CREATE TABLE IF NOT EXISTS transmuting_tiers (
|
|
min_level INTEGER NOT NULL,
|
|
max_level INTEGER NOT NULL,
|
|
fragment_id INTEGER NOT NULL,
|
|
powder_id INTEGER NOT NULL,
|
|
infusion_id INTEGER NOT NULL,
|
|
mana_id INTEGER NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
PRIMARY KEY (min_level, max_level)
|
|
)
|
|
`, &sqlitex.ExecOptions{}); err != nil {
|
|
return nil, fmt.Errorf("failed to create transmuting_tiers table: %w", err)
|
|
}
|
|
|
|
// Check if table is empty and populate with default data
|
|
var count int64
|
|
err := sqlitex.Execute(dbi.conn, "SELECT COUNT(*) FROM transmuting_tiers", &sqlitex.ExecOptions{
|
|
ResultFunc: func(stmt *sqlite.Stmt) error {
|
|
count = stmt.ColumnInt64(0)
|
|
return nil
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to count transmuting tiers: %w", err)
|
|
}
|
|
|
|
// If empty, populate with default EQ2 transmuting tiers
|
|
if count == 0 {
|
|
if err := dbi.populateDefaultTiers(); err != nil {
|
|
return nil, fmt.Errorf("failed to populate default tiers: %w", err)
|
|
}
|
|
}
|
|
|
|
// Load all tiers from database
|
|
var tiers []*TransmutingTier
|
|
err = sqlitex.Execute(dbi.conn, "SELECT min_level, max_level, fragment_id, powder_id, infusion_id, mana_id FROM transmuting_tiers ORDER BY min_level", &sqlitex.ExecOptions{
|
|
ResultFunc: func(stmt *sqlite.Stmt) error {
|
|
tier := &TransmutingTier{
|
|
MinLevel: int32(stmt.ColumnInt64(0)),
|
|
MaxLevel: int32(stmt.ColumnInt64(1)),
|
|
FragmentID: int32(stmt.ColumnInt64(2)),
|
|
PowderID: int32(stmt.ColumnInt64(3)),
|
|
InfusionID: int32(stmt.ColumnInt64(4)),
|
|
ManaID: int32(stmt.ColumnInt64(5)),
|
|
}
|
|
tiers = append(tiers, tier)
|
|
return nil
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load transmuting tiers: %w", err)
|
|
}
|
|
|
|
return tiers, nil
|
|
}
|
|
|
|
// populateDefaultTiers populates the database with default transmuting tiers
|
|
func (dbi *DatabaseImpl) populateDefaultTiers() error {
|
|
defaultTiers := []struct {
|
|
minLevel, maxLevel int32
|
|
fragmentID, powderID, infusionID, manaID int32
|
|
}{
|
|
{1, 9, 1001, 1002, 1003, 1004},
|
|
{10, 19, 1005, 1006, 1007, 1008},
|
|
{20, 29, 1009, 1010, 1011, 1012},
|
|
{30, 39, 1013, 1014, 1015, 1016},
|
|
{40, 49, 1017, 1018, 1019, 1020},
|
|
{50, 59, 1021, 1022, 1023, 1024},
|
|
{60, 69, 1025, 1026, 1027, 1028},
|
|
{70, 79, 1029, 1030, 1031, 1032},
|
|
{80, 89, 1033, 1034, 1035, 1036},
|
|
{90, 100, 1037, 1038, 1039, 1040},
|
|
}
|
|
|
|
// Use transaction for atomic inserts
|
|
endFn, err := sqlitex.ImmediateTransaction(dbi.conn)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start transaction: %w", err)
|
|
}
|
|
defer endFn(&err)
|
|
|
|
for _, tier := range defaultTiers {
|
|
err = sqlitex.Execute(dbi.conn, `
|
|
INSERT INTO transmuting_tiers (min_level, max_level, fragment_id, powder_id, infusion_id, mana_id)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
`, &sqlitex.ExecOptions{
|
|
Args: []any{tier.minLevel, tier.maxLevel, tier.fragmentID, tier.powderID, tier.infusionID, tier.manaID},
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to insert tier %d-%d: %w", tier.minLevel, tier.maxLevel, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// TODO: When integrating with a real database system, replace this with actual database queries
|
|
// Example SQL implementation would look like:
|
|
/*
|
|
func (db *DatabaseImpl) LoadTransmutingTiers() ([]*TransmutingTier, error) {
|
|
query := `SELECT min_level, max_level, fragment, powder, infusion, mana FROM transmuting ORDER BY min_level`
|
|
|
|
rows, err := db.connection.Query(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to query transmuting tiers: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var tiers []*TransmutingTier
|
|
|
|
for rows.Next() {
|
|
tier := &TransmutingTier{}
|
|
err := rows.Scan(
|
|
&tier.MinLevel,
|
|
&tier.MaxLevel,
|
|
&tier.FragmentID,
|
|
&tier.PowderID,
|
|
&tier.InfusionID,
|
|
&tier.ManaID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan transmuting tier: %w", err)
|
|
}
|
|
|
|
tiers = append(tiers, tier)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating transmuting tiers: %w", err)
|
|
}
|
|
|
|
return tiers, nil
|
|
}
|
|
*/
|
|
|
|
// SaveTransmutingTier saves a transmuting tier to the database
|
|
func (dbi *DatabaseImpl) SaveTransmutingTier(tier *TransmutingTier) error {
|
|
if tier == nil {
|
|
return fmt.Errorf("tier cannot be nil")
|
|
}
|
|
|
|
// Validate tier data
|
|
if tier.MinLevel <= 0 || tier.MaxLevel <= 0 {
|
|
return fmt.Errorf("invalid level range: %d-%d", tier.MinLevel, tier.MaxLevel)
|
|
}
|
|
|
|
if tier.MinLevel > tier.MaxLevel {
|
|
return fmt.Errorf("min level (%d) cannot be greater than max level (%d)", tier.MinLevel, tier.MaxLevel)
|
|
}
|
|
|
|
if tier.FragmentID <= 0 || tier.PowderID <= 0 || tier.InfusionID <= 0 || tier.ManaID <= 0 {
|
|
return fmt.Errorf("all material IDs must be positive")
|
|
}
|
|
|
|
err := sqlitex.Execute(dbi.conn, `
|
|
INSERT OR REPLACE INTO transmuting_tiers (min_level, max_level, fragment_id, powder_id, infusion_id, mana_id)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
`, &sqlitex.ExecOptions{
|
|
Args: []any{tier.MinLevel, tier.MaxLevel, tier.FragmentID, tier.PowderID, tier.InfusionID, tier.ManaID},
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save transmuting tier %d-%d: %w", tier.MinLevel, tier.MaxLevel, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteTransmutingTier deletes a transmuting tier from the database
|
|
func (dbi *DatabaseImpl) DeleteTransmutingTier(minLevel, maxLevel int32) error {
|
|
if minLevel <= 0 || maxLevel <= 0 {
|
|
return fmt.Errorf("invalid level range: %d-%d", minLevel, maxLevel)
|
|
}
|
|
|
|
err := sqlitex.Execute(dbi.conn, "DELETE FROM transmuting_tiers WHERE min_level = ? AND max_level = ?", &sqlitex.ExecOptions{
|
|
Args: []any{minLevel, maxLevel},
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete transmuting tier %d-%d: %w", minLevel, maxLevel, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetTransmutingTierByLevel gets a specific transmuting tier by level range
|
|
func (dbi *DatabaseImpl) GetTransmutingTierByLevel(itemLevel int32) (*TransmutingTier, error) {
|
|
var tier *TransmutingTier
|
|
|
|
err := sqlitex.Execute(dbi.conn, "SELECT min_level, max_level, fragment_id, powder_id, infusion_id, mana_id FROM transmuting_tiers WHERE min_level <= ? AND max_level >= ?", &sqlitex.ExecOptions{
|
|
Args: []any{itemLevel, itemLevel},
|
|
ResultFunc: func(stmt *sqlite.Stmt) error {
|
|
tier = &TransmutingTier{
|
|
MinLevel: int32(stmt.ColumnInt64(0)),
|
|
MaxLevel: int32(stmt.ColumnInt64(1)),
|
|
FragmentID: int32(stmt.ColumnInt64(2)),
|
|
PowderID: int32(stmt.ColumnInt64(3)),
|
|
InfusionID: int32(stmt.ColumnInt64(4)),
|
|
ManaID: int32(stmt.ColumnInt64(5)),
|
|
}
|
|
return nil
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to query transmuting tier for level %d: %w", itemLevel, err)
|
|
}
|
|
|
|
if tier == nil {
|
|
return nil, fmt.Errorf("no transmuting tier found for level %d", itemLevel)
|
|
}
|
|
|
|
return tier, nil
|
|
}
|
|
|
|
// UpdateTransmutingTier updates an existing transmuting tier
|
|
func (dbi *DatabaseImpl) UpdateTransmutingTier(oldMinLevel, oldMaxLevel int32, newTier *TransmutingTier) error {
|
|
if newTier == nil {
|
|
return fmt.Errorf("new tier cannot be nil")
|
|
}
|
|
|
|
// Validate tier data first
|
|
if newTier.MinLevel <= 0 || newTier.MaxLevel <= 0 {
|
|
return fmt.Errorf("invalid level range: %d-%d", newTier.MinLevel, newTier.MaxLevel)
|
|
}
|
|
|
|
if newTier.MinLevel > newTier.MaxLevel {
|
|
return fmt.Errorf("min level (%d) cannot be greater than max level (%d)", newTier.MinLevel, newTier.MaxLevel)
|
|
}
|
|
|
|
if newTier.FragmentID <= 0 || newTier.PowderID <= 0 || newTier.InfusionID <= 0 || newTier.ManaID <= 0 {
|
|
return fmt.Errorf("all material IDs must be positive")
|
|
}
|
|
|
|
err := sqlitex.Execute(dbi.conn, `
|
|
UPDATE transmuting_tiers
|
|
SET min_level=?, max_level=?, fragment_id=?, powder_id=?, infusion_id=?, mana_id=?
|
|
WHERE min_level=? AND max_level=?
|
|
`, &sqlitex.ExecOptions{
|
|
Args: []any{newTier.MinLevel, newTier.MaxLevel, newTier.FragmentID, newTier.PowderID,
|
|
newTier.InfusionID, newTier.ManaID, oldMinLevel, oldMaxLevel},
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update transmuting tier %d-%d: %w", oldMinLevel, oldMaxLevel, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TransmutingTierExists checks if a transmuting tier exists for the given level range
|
|
func (dbi *DatabaseImpl) TransmutingTierExists(minLevel, maxLevel int32) (bool, error) {
|
|
var count int64
|
|
|
|
err := sqlitex.Execute(dbi.conn, "SELECT COUNT(*) FROM transmuting_tiers WHERE min_level = ? AND max_level = ?", &sqlitex.ExecOptions{
|
|
Args: []any{minLevel, maxLevel},
|
|
ResultFunc: func(stmt *sqlite.Stmt) error {
|
|
count = stmt.ColumnInt64(0)
|
|
return nil
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to check tier existence: %w", err)
|
|
}
|
|
|
|
return count > 0, nil
|
|
}
|