package collections import ( "fmt" "eq2emu/internal/database" ) // PlayerList manages collections for a specific player type PlayerList struct { CharacterID int32 collections map[int32]*Collection db *database.Database } // NewPlayerList creates a new player collection list func NewPlayerList(characterID int32, db *database.Database) *PlayerList { return &PlayerList{ CharacterID: characterID, collections: make(map[int32]*Collection), db: db, } } // AddCollection adds a collection to the player's list func (pl *PlayerList) AddCollection(collection *Collection) bool { if collection == nil { return false } if _, exists := pl.collections[collection.GetID()]; exists { return false // Already exists } pl.collections[collection.GetID()] = collection return true } // GetCollection retrieves a collection by ID func (pl *PlayerList) GetCollection(id int32) *Collection { return pl.collections[id] } // RemoveCollection removes a collection from the player's list func (pl *PlayerList) RemoveCollection(id int32) bool { if _, exists := pl.collections[id]; exists { delete(pl.collections, id) return true } return false } // ClearCollections removes all collections from the player's list func (pl *PlayerList) ClearCollections() { pl.collections = make(map[int32]*Collection) } // Size returns the number of collections the player has func (pl *PlayerList) Size() int { return len(pl.collections) } // GetCollections returns all player collections func (pl *PlayerList) GetCollections() map[int32]*Collection { return pl.collections } // NeedsItem checks if any of the player's collections need the specified item func (pl *PlayerList) NeedsItem(itemID int32) bool { for _, collection := range pl.collections { if collection.NeedsItem(itemID) { return true } } return false } // HasCollectionsToHandIn checks if the player has any collections ready to turn in func (pl *PlayerList) HasCollectionsToHandIn() bool { for _, collection := range pl.collections { if collection.GetIsReadyToTurnIn() { return true } } return false } // GetCollectionsToHandIn returns all collections ready to turn in func (pl *PlayerList) GetCollectionsToHandIn() []*Collection { var readyCollections []*Collection for _, collection := range pl.collections { if collection.GetIsReadyToTurnIn() { readyCollections = append(readyCollections, collection) } } return readyCollections } // GetCompletedCollections returns all completed collections func (pl *PlayerList) GetCompletedCollections() []*Collection { var completedCollections []*Collection for _, collection := range pl.collections { if collection.Completed { completedCollections = append(completedCollections, collection) } } return completedCollections } // GetActiveCollections returns all non-completed collections func (pl *PlayerList) GetActiveCollections() []*Collection { var activeCollections []*Collection for _, collection := range pl.collections { if !collection.Completed { activeCollections = append(activeCollections, collection) } } return activeCollections } // LoadPlayerCollections loads all collections for the player from database func (pl *PlayerList) LoadPlayerCollections(masterList *MasterList) error { if pl.db == nil { return fmt.Errorf("no database connection available") } // Clear existing collections pl.ClearCollections() // Load player's collection progress query := `SELECT collection_id, completed FROM character_collections WHERE char_id = ?` rows, err := pl.db.Query(query, pl.CharacterID) if err != nil { return fmt.Errorf("failed to load player collections: %w", err) } defer rows.Close() for rows.Next() { var collectionID int32 var completed bool if err := rows.Scan(&collectionID, &completed); err != nil { return fmt.Errorf("failed to scan player collection: %w", err) } // Get the master collection masterCollection := masterList.GetCollection(collectionID) if masterCollection == nil { continue // Skip if collection doesn't exist in master list } // Create a copy for the player playerCollection := masterCollection.Clone() playerCollection.Completed = completed // Load player's found items itemQuery := `SELECT collection_item_id FROM character_collection_items WHERE char_id = ? AND collection_id = ?` itemRows, err := pl.db.Query(itemQuery, pl.CharacterID, collectionID) if err != nil { return fmt.Errorf("failed to load player collection items: %w", err) } for itemRows.Next() { var itemID int32 if err := itemRows.Scan(&itemID); err != nil { itemRows.Close() return fmt.Errorf("failed to scan player collection item: %w", err) } // Mark the item as found if collectionItem := playerCollection.GetCollectionItemByItemID(itemID); collectionItem != nil { collectionItem.Found = 1 } } itemRows.Close() pl.AddCollection(playerCollection) } return nil } // SavePlayerCollection saves a player's collection progress func (pl *PlayerList) SavePlayerCollection(collectionID int32) error { if pl.db == nil { return fmt.Errorf("no database connection available") } collection := pl.GetCollection(collectionID) if collection == nil { return fmt.Errorf("collection %d not found for player %d", collectionID, pl.CharacterID) } // Update or insert player collection record _, err := pl.db.Exec(` INSERT INTO character_collections (char_id, collection_id, completed) VALUES (?, ?, ?) ON CONFLICT(char_id, collection_id) DO UPDATE SET completed = ?`, pl.CharacterID, collectionID, collection.Completed, collection.Completed) if err != nil { return fmt.Errorf("failed to save player collection: %w", err) } // Delete existing found items and re-insert _, err = pl.db.Exec(`DELETE FROM character_collection_items WHERE char_id = ? AND collection_id = ?`, pl.CharacterID, collectionID) if err != nil { return fmt.Errorf("failed to delete old collection items: %w", err) } // Insert found items for _, item := range collection.CollectionItems { if item.Found != 0 { _, err = pl.db.Exec(` INSERT INTO character_collection_items (char_id, collection_id, collection_item_id) VALUES (?, ?, ?)`, pl.CharacterID, collectionID, item.ItemID) if err != nil { return fmt.Errorf("failed to save collection item: %w", err) } } } collection.SaveNeeded = false return nil } // SaveAllCollections saves all player collections that need saving func (pl *PlayerList) SaveAllCollections() error { for collectionID, collection := range pl.collections { if collection.SaveNeeded { if err := pl.SavePlayerCollection(collectionID); err != nil { return err } } } return nil } // GetStatistics returns statistics about the player's collections func (pl *PlayerList) GetStatistics() map[string]any { stats := make(map[string]any) stats["total_collections"] = len(pl.collections) completed := 0 readyToTurnIn := 0 totalItemsFound := 0 totalItemsNeeded := 0 for _, collection := range pl.collections { if collection.Completed { completed++ } if collection.GetIsReadyToTurnIn() { readyToTurnIn++ } for _, item := range collection.CollectionItems { if item.Found != 0 { totalItemsFound++ } else { totalItemsNeeded++ } } } stats["completed_collections"] = completed stats["ready_to_turn_in"] = readyToTurnIn stats["active_collections"] = len(pl.collections) - completed stats["total_items_found"] = totalItemsFound stats["total_items_needed"] = totalItemsNeeded if totalItemsFound+totalItemsNeeded > 0 { stats["overall_progress"] = float64(totalItemsFound) / float64(totalItemsFound+totalItemsNeeded) * 100.0 } else { stats["overall_progress"] = 0.0 } return stats }