430 lines
11 KiB
Go
430 lines
11 KiB
Go
package tradeskills
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"log"
|
|
)
|
|
|
|
// DatabaseService handles database operations for the tradeskills system.
|
|
type DatabaseService interface {
|
|
// LoadTradeskillEvents loads all tradeskill events from the database
|
|
LoadTradeskillEvents(masterList *MasterTradeskillEventsList) error
|
|
|
|
// CreateTradeskillTables creates the required database tables
|
|
CreateTradeskillTables() error
|
|
|
|
// SaveTradeskillEvent saves a tradeskill event to the database
|
|
SaveTradeskillEvent(event *TradeskillEvent) error
|
|
|
|
// DeleteTradeskillEvent removes a tradeskill event from the database
|
|
DeleteTradeskillEvent(name string, technique uint32) error
|
|
}
|
|
|
|
// SQLiteTradeskillDatabase implements DatabaseService for SQLite.
|
|
type SQLiteTradeskillDatabase struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// NewSQLiteTradeskillDatabase creates a new SQLite database service.
|
|
func NewSQLiteTradeskillDatabase(db *sql.DB) *SQLiteTradeskillDatabase {
|
|
return &SQLiteTradeskillDatabase{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
// CreateTradeskillTables creates the required database tables for tradeskills.
|
|
func (db *SQLiteTradeskillDatabase) CreateTradeskillTables() error {
|
|
createTableSQL := `
|
|
CREATE TABLE IF NOT EXISTS tradeskillevents (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
icon INTEGER NOT NULL,
|
|
technique INTEGER NOT NULL,
|
|
success_progress INTEGER NOT NULL DEFAULT 0,
|
|
success_durability INTEGER NOT NULL DEFAULT 0,
|
|
success_hp INTEGER NOT NULL DEFAULT 0,
|
|
success_power INTEGER NOT NULL DEFAULT 0,
|
|
success_spell_id INTEGER NOT NULL DEFAULT 0,
|
|
success_item_id INTEGER NOT NULL DEFAULT 0,
|
|
fail_progress INTEGER NOT NULL DEFAULT 0,
|
|
fail_durability INTEGER NOT NULL DEFAULT 0,
|
|
fail_hp INTEGER NOT NULL DEFAULT 0,
|
|
fail_power INTEGER NOT NULL DEFAULT 0,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(name, technique)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_tradeskillevents_technique ON tradeskillevents(technique);
|
|
CREATE INDEX IF NOT EXISTS idx_tradeskillevents_name ON tradeskillevents(name);
|
|
`
|
|
|
|
_, err := db.db.Exec(createTableSQL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create tradeskillevents table: %w", err)
|
|
}
|
|
|
|
log.Printf("Created tradeskillevents table")
|
|
return nil
|
|
}
|
|
|
|
// LoadTradeskillEvents loads all tradeskill events from the database into the master list.
|
|
func (db *SQLiteTradeskillDatabase) LoadTradeskillEvents(masterList *MasterTradeskillEventsList) error {
|
|
if masterList == nil {
|
|
return fmt.Errorf("masterList cannot be nil")
|
|
}
|
|
|
|
query := `
|
|
SELECT name, icon, technique, success_progress, success_durability,
|
|
success_hp, success_power, success_spell_id, success_item_id,
|
|
fail_progress, fail_durability, fail_hp, fail_power
|
|
FROM tradeskillevents
|
|
ORDER BY technique, name
|
|
`
|
|
|
|
rows, err := db.db.Query(query)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to query tradeskillevents: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
eventsLoaded := 0
|
|
|
|
for rows.Next() {
|
|
event := &TradeskillEvent{}
|
|
|
|
err := rows.Scan(
|
|
&event.Name,
|
|
&event.Icon,
|
|
&event.Technique,
|
|
&event.SuccessProgress,
|
|
&event.SuccessDurability,
|
|
&event.SuccessHP,
|
|
&event.SuccessPower,
|
|
&event.SuccessSpellID,
|
|
&event.SuccessItemID,
|
|
&event.FailProgress,
|
|
&event.FailDurability,
|
|
&event.FailHP,
|
|
&event.FailPower,
|
|
)
|
|
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to scan tradeskill event row: %v", err)
|
|
continue
|
|
}
|
|
|
|
// Validate the event
|
|
if event.Name == "" {
|
|
log.Printf("Warning: Skipping tradeskill event with empty name")
|
|
continue
|
|
}
|
|
|
|
if !IsValidTechnique(event.Technique) {
|
|
log.Printf("Warning: Skipping tradeskill event '%s' with invalid technique %d",
|
|
event.Name, event.Technique)
|
|
continue
|
|
}
|
|
|
|
masterList.AddEvent(event)
|
|
eventsLoaded++
|
|
|
|
log.Printf("Loaded tradeskill event: %s (technique: %d)", event.Name, event.Technique)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return fmt.Errorf("error iterating tradeskill events: %w", err)
|
|
}
|
|
|
|
log.Printf("Loaded %d tradeskill events", eventsLoaded)
|
|
return nil
|
|
}
|
|
|
|
// SaveTradeskillEvent saves a tradeskill event to the database.
|
|
func (db *SQLiteTradeskillDatabase) SaveTradeskillEvent(event *TradeskillEvent) error {
|
|
if event == nil {
|
|
return fmt.Errorf("event cannot be nil")
|
|
}
|
|
|
|
if event.Name == "" {
|
|
return fmt.Errorf("event name cannot be empty")
|
|
}
|
|
|
|
if !IsValidTechnique(event.Technique) {
|
|
return fmt.Errorf("invalid technique: %d", event.Technique)
|
|
}
|
|
|
|
insertSQL := `
|
|
INSERT OR REPLACE INTO tradeskillevents (
|
|
name, icon, technique, success_progress, success_durability,
|
|
success_hp, success_power, success_spell_id, success_item_id,
|
|
fail_progress, fail_durability, fail_hp, fail_power, updated_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
`
|
|
|
|
_, err := db.db.Exec(insertSQL,
|
|
event.Name,
|
|
event.Icon,
|
|
event.Technique,
|
|
event.SuccessProgress,
|
|
event.SuccessDurability,
|
|
event.SuccessHP,
|
|
event.SuccessPower,
|
|
event.SuccessSpellID,
|
|
event.SuccessItemID,
|
|
event.FailProgress,
|
|
event.FailDurability,
|
|
event.FailHP,
|
|
event.FailPower,
|
|
)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save tradeskill event '%s': %w", event.Name, err)
|
|
}
|
|
|
|
log.Printf("Saved tradeskill event: %s", event.Name)
|
|
return nil
|
|
}
|
|
|
|
// DeleteTradeskillEvent removes a tradeskill event from the database.
|
|
func (db *SQLiteTradeskillDatabase) DeleteTradeskillEvent(name string, technique uint32) error {
|
|
if name == "" {
|
|
return fmt.Errorf("name cannot be empty")
|
|
}
|
|
|
|
deleteSQL := `DELETE FROM tradeskillevents WHERE name = ? AND technique = ?`
|
|
|
|
result, err := db.db.Exec(deleteSQL, name, technique)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete tradeskill event '%s': %w", name, err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected for event '%s': %w", name, err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return fmt.Errorf("tradeskill event '%s' with technique %d not found", name, technique)
|
|
}
|
|
|
|
log.Printf("Deleted tradeskill event: %s (technique: %d)", name, technique)
|
|
return nil
|
|
}
|
|
|
|
// GetTradeskillEventsByTechnique retrieves all events for a specific technique from the database.
|
|
func (db *SQLiteTradeskillDatabase) GetTradeskillEventsByTechnique(technique uint32) ([]*TradeskillEvent, error) {
|
|
if !IsValidTechnique(technique) {
|
|
return nil, fmt.Errorf("invalid technique: %d", technique)
|
|
}
|
|
|
|
query := `
|
|
SELECT name, icon, technique, success_progress, success_durability,
|
|
success_hp, success_power, success_spell_id, success_item_id,
|
|
fail_progress, fail_durability, fail_hp, fail_power
|
|
FROM tradeskillevents
|
|
WHERE technique = ?
|
|
ORDER BY name
|
|
`
|
|
|
|
rows, err := db.db.Query(query, technique)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to query events for technique %d: %w", technique, err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var events []*TradeskillEvent
|
|
|
|
for rows.Next() {
|
|
event := &TradeskillEvent{}
|
|
|
|
err := rows.Scan(
|
|
&event.Name,
|
|
&event.Icon,
|
|
&event.Technique,
|
|
&event.SuccessProgress,
|
|
&event.SuccessDurability,
|
|
&event.SuccessHP,
|
|
&event.SuccessPower,
|
|
&event.SuccessSpellID,
|
|
&event.SuccessItemID,
|
|
&event.FailProgress,
|
|
&event.FailDurability,
|
|
&event.FailHP,
|
|
&event.FailPower,
|
|
)
|
|
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to scan event row: %v", err)
|
|
continue
|
|
}
|
|
|
|
events = append(events, event)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating events for technique %d: %w", technique, err)
|
|
}
|
|
|
|
return events, nil
|
|
}
|
|
|
|
// GetTradeskillEventByName retrieves a specific event by name and technique.
|
|
func (db *SQLiteTradeskillDatabase) GetTradeskillEventByName(name string, technique uint32) (*TradeskillEvent, error) {
|
|
if name == "" {
|
|
return nil, fmt.Errorf("name cannot be empty")
|
|
}
|
|
|
|
if !IsValidTechnique(technique) {
|
|
return nil, fmt.Errorf("invalid technique: %d", technique)
|
|
}
|
|
|
|
query := `
|
|
SELECT name, icon, technique, success_progress, success_durability,
|
|
success_hp, success_power, success_spell_id, success_item_id,
|
|
fail_progress, fail_durability, fail_hp, fail_power
|
|
FROM tradeskillevents
|
|
WHERE name = ? AND technique = ?
|
|
`
|
|
|
|
row := db.db.QueryRow(query, name, technique)
|
|
|
|
event := &TradeskillEvent{}
|
|
err := row.Scan(
|
|
&event.Name,
|
|
&event.Icon,
|
|
&event.Technique,
|
|
&event.SuccessProgress,
|
|
&event.SuccessDurability,
|
|
&event.SuccessHP,
|
|
&event.SuccessPower,
|
|
&event.SuccessSpellID,
|
|
&event.SuccessItemID,
|
|
&event.FailProgress,
|
|
&event.FailDurability,
|
|
&event.FailHP,
|
|
&event.FailPower,
|
|
)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("tradeskill event '%s' with technique %d not found", name, technique)
|
|
}
|
|
return nil, fmt.Errorf("failed to get tradeskill event '%s': %w", name, err)
|
|
}
|
|
|
|
return event, nil
|
|
}
|
|
|
|
// CountTradeskillEvents returns the total number of events in the database.
|
|
func (db *SQLiteTradeskillDatabase) CountTradeskillEvents() (int32, error) {
|
|
query := `SELECT COUNT(*) FROM tradeskillevents`
|
|
|
|
var count int32
|
|
err := db.db.QueryRow(query).Scan(&count)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to count tradeskill events: %w", err)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
// GetTechniqueCounts returns the number of events per technique.
|
|
func (db *SQLiteTradeskillDatabase) GetTechniqueCounts() (map[uint32]int32, error) {
|
|
query := `
|
|
SELECT technique, COUNT(*) as count
|
|
FROM tradeskillevents
|
|
GROUP BY technique
|
|
ORDER BY technique
|
|
`
|
|
|
|
rows, err := db.db.Query(query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to query technique counts: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
counts := make(map[uint32]int32)
|
|
|
|
for rows.Next() {
|
|
var technique uint32
|
|
var count int32
|
|
|
|
err := rows.Scan(&technique, &count)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to scan technique count row: %v", err)
|
|
continue
|
|
}
|
|
|
|
counts[technique] = count
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("error iterating technique counts: %w", err)
|
|
}
|
|
|
|
return counts, nil
|
|
}
|
|
|
|
// InsertDefaultTradeskillEvents adds some default tradeskill events to the database.
|
|
func (db *SQLiteTradeskillDatabase) InsertDefaultTradeskillEvents() error {
|
|
defaultEvents := []*TradeskillEvent{
|
|
{
|
|
Name: "Blazing Heat",
|
|
Icon: 1234,
|
|
Technique: TechniqueSkillAlchemy,
|
|
SuccessProgress: 25,
|
|
SuccessDurability: 0,
|
|
SuccessHP: 0,
|
|
SuccessPower: -10,
|
|
SuccessSpellID: 0,
|
|
SuccessItemID: 0,
|
|
FailProgress: -10,
|
|
FailDurability: -25,
|
|
FailHP: 0,
|
|
FailPower: 0,
|
|
},
|
|
{
|
|
Name: "Precise Cut",
|
|
Icon: 5678,
|
|
Technique: TechniqueSkillFletching,
|
|
SuccessProgress: 30,
|
|
SuccessDurability: 5,
|
|
SuccessHP: 0,
|
|
SuccessPower: -5,
|
|
SuccessSpellID: 0,
|
|
SuccessItemID: 0,
|
|
FailProgress: 0,
|
|
FailDurability: -30,
|
|
FailHP: 0,
|
|
FailPower: 0,
|
|
},
|
|
{
|
|
Name: "Perfect Stitch",
|
|
Icon: 9012,
|
|
Technique: TechniqueSkillTailoring,
|
|
SuccessProgress: 20,
|
|
SuccessDurability: 10,
|
|
SuccessHP: 0,
|
|
SuccessPower: -8,
|
|
SuccessSpellID: 0,
|
|
SuccessItemID: 0,
|
|
FailProgress: -5,
|
|
FailDurability: -15,
|
|
FailHP: 0,
|
|
FailPower: 0,
|
|
},
|
|
}
|
|
|
|
for _, event := range defaultEvents {
|
|
err := db.SaveTradeskillEvent(event)
|
|
if err != nil {
|
|
log.Printf("Warning: Failed to insert default event '%s': %v", event.Name, err)
|
|
}
|
|
}
|
|
|
|
log.Printf("Inserted %d default tradeskill events", len(defaultEvents))
|
|
return nil
|
|
}
|