package achievements import ( "eq2emu/internal/database" "fmt" "time" ) // LoadAllAchievements loads all achievements from database into master list func LoadAllAchievements(db *database.DB, masterList *MasterList) error { query := `SELECT achievement_id, title, uncompleted_text, completed_text, category, expansion, icon, point_value, qty_req, hide_achievement, unknown3a, unknown3b FROM achievements` err := db.Query(query, func(row *database.Row) error { achievement := NewAchievement() achievement.ID = uint32(row.Int(0)) achievement.Title = row.Text(1) achievement.UncompletedText = row.Text(2) achievement.CompletedText = row.Text(3) achievement.Category = row.Text(4) achievement.Expansion = row.Text(5) achievement.Icon = uint16(row.Int(6)) achievement.PointValue = uint32(row.Int(7)) achievement.QtyRequired = uint32(row.Int(8)) achievement.Hide = row.Bool(9) achievement.Unknown3A = uint32(row.Int(10)) achievement.Unknown3B = uint32(row.Int(11)) // Load requirements and rewards if err := loadAchievementRequirements(db, achievement); err != nil { return fmt.Errorf("failed to load requirements for achievement %d: %w", achievement.ID, err) } if err := loadAchievementRewards(db, achievement); err != nil { return fmt.Errorf("failed to load rewards for achievement %d: %w", achievement.ID, err) } if !masterList.AddAchievement(achievement) { return fmt.Errorf("duplicate achievement ID: %d", achievement.ID) } return nil }) return err } // loadAchievementRequirements loads requirements for a specific achievement func loadAchievementRequirements(db *database.DB, achievement *Achievement) error { query := `SELECT achievement_id, name, qty_req FROM achievements_requirements WHERE achievement_id = ?` return db.Query(query, func(row *database.Row) error { req := Requirement{ AchievementID: uint32(row.Int(0)), Name: row.Text(1), QtyRequired: uint32(row.Int(2)), } achievement.AddRequirement(req) return nil }, achievement.ID) } // loadAchievementRewards loads rewards for a specific achievement func loadAchievementRewards(db *database.DB, achievement *Achievement) error { query := `SELECT achievement_id, reward FROM achievements_rewards WHERE achievement_id = ?` return db.Query(query, func(row *database.Row) error { reward := Reward{ AchievementID: uint32(row.Int(0)), Reward: row.Text(1), } achievement.AddReward(reward) return nil }, achievement.ID) } // LoadPlayerAchievements loads player achievements from database func LoadPlayerAchievements(db *database.DB, playerID uint32, playerList *PlayerList) error { query := `SELECT achievement_id, title, uncompleted_text, completed_text, category, expansion, icon, point_value, qty_req, hide_achievement, unknown3a, unknown3b FROM achievements` err := db.Query(query, func(row *database.Row) error { achievement := NewAchievement() achievement.ID = uint32(row.Int(0)) achievement.Title = row.Text(1) achievement.UncompletedText = row.Text(2) achievement.CompletedText = row.Text(3) achievement.Category = row.Text(4) achievement.Expansion = row.Text(5) achievement.Icon = uint16(row.Int(6)) achievement.PointValue = uint32(row.Int(7)) achievement.QtyRequired = uint32(row.Int(8)) achievement.Hide = row.Bool(9) achievement.Unknown3A = uint32(row.Int(10)) achievement.Unknown3B = uint32(row.Int(11)) // Load requirements and rewards if err := loadAchievementRequirements(db, achievement); err != nil { return fmt.Errorf("failed to load requirements: %w", err) } if err := loadAchievementRewards(db, achievement); err != nil { return fmt.Errorf("failed to load rewards: %w", err) } if !playerList.AddAchievement(achievement) { return fmt.Errorf("duplicate achievement ID: %d", achievement.ID) } return nil }) return err } // LoadPlayerAchievementUpdates loads player achievement progress from database func LoadPlayerAchievementUpdates(db *database.DB, playerID uint32, updateList *PlayerUpdateList) error { query := `SELECT char_id, achievement_id, completed_date FROM character_achievements WHERE char_id = ?` return db.Query(query, func(row *database.Row) error { update := NewUpdate() update.ID = uint32(row.Int(1)) // Convert completed_date from Unix timestamp if !row.IsNull(2) { timestamp := row.Int64(2) update.CompletedDate = time.Unix(timestamp, 0) } // Load update items if err := loadPlayerAchievementUpdateItems(db, playerID, update); err != nil { return fmt.Errorf("failed to load update items: %w", err) } if !updateList.AddUpdate(update) { return fmt.Errorf("duplicate achievement update ID: %d", update.ID) } return nil }, playerID) } // loadPlayerAchievementUpdateItems loads progress items for an achievement update func loadPlayerAchievementUpdateItems(db *database.DB, playerID uint32, update *Update) error { query := `SELECT achievement_id, items FROM character_achievements_items WHERE char_id = ? AND achievement_id = ?` return db.Query(query, func(row *database.Row) error { item := UpdateItem{ AchievementID: uint32(row.Int(0)), ItemUpdate: uint32(row.Int(1)), } update.AddUpdateItem(item) return nil }, playerID, update.ID) } // SavePlayerAchievementUpdate saves or updates player achievement progress func SavePlayerAchievementUpdate(db *database.DB, playerID uint32, update *Update) error { return db.Transaction(func(tx *database.DB) error { // Save or update main achievement record query := `INSERT OR REPLACE INTO character_achievements (char_id, achievement_id, completed_date) VALUES (?, ?, ?)` var completedDate *int64 if !update.CompletedDate.IsZero() { timestamp := update.CompletedDate.Unix() completedDate = ×tamp } if err := tx.Exec(query, playerID, update.ID, completedDate); err != nil { return fmt.Errorf("failed to save achievement update: %w", err) } // Delete existing update items deleteQuery := `DELETE FROM character_achievements_items WHERE char_id = ? AND achievement_id = ?` if err := tx.Exec(deleteQuery, playerID, update.ID); err != nil { return fmt.Errorf("failed to delete old update items: %w", err) } // Insert new update items itemQuery := `INSERT INTO character_achievements_items (char_id, achievement_id, items) VALUES (?, ?, ?)` for _, item := range update.UpdateItems { if err := tx.Exec(itemQuery, playerID, item.AchievementID, item.ItemUpdate); err != nil { return fmt.Errorf("failed to save update item: %w", err) } } return nil }) } // DeletePlayerAchievementUpdate removes player achievement progress from database func DeletePlayerAchievementUpdate(db *database.DB, playerID uint32, achievementID uint32) error { return db.Transaction(func(tx *database.DB) error { // Delete main achievement record query := `DELETE FROM character_achievements WHERE char_id = ? AND achievement_id = ?` if err := tx.Exec(query, playerID, achievementID); err != nil { return fmt.Errorf("failed to delete achievement update: %w", err) } // Delete update items itemQuery := `DELETE FROM character_achievements_items WHERE char_id = ? AND achievement_id = ?` if err := tx.Exec(itemQuery, playerID, achievementID); err != nil { return fmt.Errorf("failed to delete update items: %w", err) } return nil }) } // SaveAchievement saves or updates an achievement in the database func SaveAchievement(db *database.DB, achievement *Achievement) error { return db.Transaction(func(tx *database.DB) error { // Save main achievement record query := `INSERT OR REPLACE INTO achievements (achievement_id, title, uncompleted_text, completed_text, category, expansion, icon, point_value, qty_req, hide_achievement, unknown3a, unknown3b) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` if err := tx.Exec(query, achievement.ID, achievement.Title, achievement.UncompletedText, achievement.CompletedText, achievement.Category, achievement.Expansion, achievement.Icon, achievement.PointValue, achievement.QtyRequired, achievement.Hide, achievement.Unknown3A, achievement.Unknown3B); err != nil { return fmt.Errorf("failed to save achievement: %w", err) } // Delete existing requirements and rewards if err := tx.Exec("DELETE FROM achievements_requirements WHERE achievement_id = ?", achievement.ID); err != nil { return fmt.Errorf("failed to delete old requirements: %w", err) } if err := tx.Exec("DELETE FROM achievements_rewards WHERE achievement_id = ?", achievement.ID); err != nil { return fmt.Errorf("failed to delete old rewards: %w", err) } // Insert requirements reqQuery := `INSERT INTO achievements_requirements (achievement_id, name, qty_req) VALUES (?, ?, ?)` for _, req := range achievement.Requirements { if err := tx.Exec(reqQuery, req.AchievementID, req.Name, req.QtyRequired); err != nil { return fmt.Errorf("failed to save requirement: %w", err) } } // Insert rewards rewardQuery := `INSERT INTO achievements_rewards (achievement_id, reward) VALUES (?, ?)` for _, reward := range achievement.Rewards { if err := tx.Exec(rewardQuery, reward.AchievementID, reward.Reward); err != nil { return fmt.Errorf("failed to save reward: %w", err) } } return nil }) } // DeleteAchievement removes an achievement and all related records from database func DeleteAchievement(db *database.DB, achievementID uint32) error { return db.Transaction(func(tx *database.DB) error { // Delete main achievement if err := tx.Exec("DELETE FROM achievements WHERE achievement_id = ?", achievementID); err != nil { return fmt.Errorf("failed to delete achievement: %w", err) } // Delete requirements if err := tx.Exec("DELETE FROM achievements_requirements WHERE achievement_id = ?", achievementID); err != nil { return fmt.Errorf("failed to delete requirements: %w", err) } // Delete rewards if err := tx.Exec("DELETE FROM achievements_rewards WHERE achievement_id = ?", achievementID); err != nil { return fmt.Errorf("failed to delete rewards: %w", err) } // Delete player progress (optional - might want to preserve history) if err := tx.Exec("DELETE FROM character_achievements WHERE achievement_id = ?", achievementID); err != nil { return fmt.Errorf("failed to delete player achievements: %w", err) } if err := tx.Exec("DELETE FROM character_achievements_items WHERE achievement_id = ?", achievementID); err != nil { return fmt.Errorf("failed to delete player achievement items: %w", err) } return nil }) }