281 lines
7.6 KiB
Go
281 lines
7.6 KiB
Go
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
|
|
} |