381 lines
12 KiB
Go

package collections
import (
"context"
"fmt"
"sync"
)
// NewCollectionManager creates a new collection manager instance
func NewCollectionManager(database CollectionDatabase, itemLookup ItemLookup) *CollectionManager {
return &CollectionManager{
masterList: NewMasterCollectionList(database),
database: database,
itemLookup: itemLookup,
}
}
// Initialize initializes the collection system by loading all collections
func (cm *CollectionManager) Initialize(ctx context.Context) error {
return cm.masterList.Initialize(ctx, cm.itemLookup)
}
// GetMasterList returns the master collection list
func (cm *CollectionManager) GetMasterList() *MasterCollectionList {
return cm.masterList
}
// CreatePlayerCollectionList creates a new player collection list
func (cm *CollectionManager) CreatePlayerCollectionList(characterID int32) *PlayerCollectionList {
return NewPlayerCollectionList(characterID, cm.database)
}
// GetCollection returns a collection by ID from the master list
func (cm *CollectionManager) GetCollection(collectionID int32) *Collection {
return cm.masterList.GetCollection(collectionID)
}
// GetCollectionCopy returns a copy of a collection by ID
func (cm *CollectionManager) GetCollectionCopy(collectionID int32) *Collection {
return cm.masterList.GetCollectionCopy(collectionID)
}
// ProcessItemFound processes when a player finds an item across all collections
func (cm *CollectionManager) ProcessItemFound(playerList *PlayerCollectionList, itemID int32) ([]*Collection, error) {
if playerList == nil {
return nil, fmt.Errorf("player collection list is nil")
}
return playerList.ProcessItemFound(itemID, cm.masterList)
}
// CompleteCollection processes collection completion for a player
func (cm *CollectionManager) CompleteCollection(playerList *PlayerCollectionList, collectionID int32, rewardProvider RewardProvider) error {
if playerList == nil {
return fmt.Errorf("player collection list is nil")
}
collection := playerList.GetCollection(collectionID)
if collection == nil {
return fmt.Errorf("collection %d not found in player list", collectionID)
}
if !collection.GetIsReadyToTurnIn() {
return fmt.Errorf("collection %d is not ready to complete", collectionID)
}
// Give rewards if provider is available
if rewardProvider != nil {
characterID := playerList.GetCharacterID()
// Give coin reward
if coin := collection.GetRewardCoin(); coin > 0 {
if err := rewardProvider.GiveCoin(characterID, coin); err != nil {
return fmt.Errorf("failed to give coin reward: %w", err)
}
}
// Give XP reward
if xp := collection.GetRewardXP(); xp > 0 {
if err := rewardProvider.GiveXP(characterID, xp); err != nil {
return fmt.Errorf("failed to give XP reward: %w", err)
}
}
// Give item rewards
for _, reward := range collection.GetRewardItems() {
if err := rewardProvider.GiveItem(characterID, reward.ItemID, reward.Quantity); err != nil {
return fmt.Errorf("failed to give item reward %d: %w", reward.ItemID, err)
}
}
}
// Mark collection as completed
return playerList.CompleteCollection(collectionID)
}
// GetAvailableCollections returns collections available to a player based on level
func (cm *CollectionManager) GetAvailableCollections(playerLevel int8) []*Collection {
return cm.masterList.GetCollectionsByLevel(0, playerLevel)
}
// GetCollectionsByCategory returns collections in a specific category
func (cm *CollectionManager) GetCollectionsByCategory(category string) []*Collection {
return cm.masterList.GetCollectionsByCategory(category)
}
// GetAllCategories returns all collection categories
func (cm *CollectionManager) GetAllCategories() []string {
return cm.masterList.GetCategories()
}
// SearchCollections searches for collections by name
func (cm *CollectionManager) SearchCollections(searchTerm string) []*Collection {
return cm.masterList.FindCollectionsByName(searchTerm)
}
// ValidateSystemIntegrity validates the integrity of the collection system
func (cm *CollectionManager) ValidateSystemIntegrity() []error {
return cm.masterList.ValidateIntegrity(cm.itemLookup)
}
// GetSystemStatistics returns overall collection system statistics
func (cm *CollectionManager) GetSystemStatistics() CollectionStatistics {
return cm.masterList.GetStatistics()
}
// CollectionService provides high-level collection system services
type CollectionService struct {
manager *CollectionManager
playerLists map[int32]*PlayerCollectionList
mu sync.RWMutex
clientManager ClientManager
eventHandler CollectionEventHandler
logger LogHandler
}
// NewCollectionService creates a new collection service
func NewCollectionService(database CollectionDatabase, itemLookup ItemLookup, clientManager ClientManager) *CollectionService {
return &CollectionService{
manager: NewCollectionManager(database, itemLookup),
playerLists: make(map[int32]*PlayerCollectionList),
clientManager: clientManager,
}
}
// SetEventHandler sets the collection event handler
func (cs *CollectionService) SetEventHandler(handler CollectionEventHandler) {
cs.eventHandler = handler
}
// SetLogger sets the logger for the service
func (cs *CollectionService) SetLogger(logger LogHandler) {
cs.logger = logger
}
// Initialize initializes the collection service
func (cs *CollectionService) Initialize(ctx context.Context) error {
return cs.manager.Initialize(ctx)
}
// LoadPlayerCollections loads collections for a specific player
func (cs *CollectionService) LoadPlayerCollections(ctx context.Context, characterID int32) error {
cs.mu.Lock()
defer cs.mu.Unlock()
playerList := cs.manager.CreatePlayerCollectionList(characterID)
if err := playerList.Initialize(ctx, cs.manager.GetMasterList()); err != nil {
return fmt.Errorf("failed to initialize player collections: %w", err)
}
cs.playerLists[characterID] = playerList
if cs.logger != nil {
cs.logger.LogDebug("collections", "Loaded %d collections for character %d",
playerList.Size(), characterID)
}
return nil
}
// UnloadPlayerCollections unloads collections for a player (when they log out)
func (cs *CollectionService) UnloadPlayerCollections(ctx context.Context, characterID int32) error {
cs.mu.Lock()
defer cs.mu.Unlock()
playerList, exists := cs.playerLists[characterID]
if !exists {
return nil // Already unloaded
}
// Save any pending changes
if err := playerList.SaveCollections(ctx); err != nil {
if cs.logger != nil {
cs.logger.LogError("collections", "Failed to save collections for character %d: %v", characterID, err)
}
return fmt.Errorf("failed to save collections: %w", err)
}
delete(cs.playerLists, characterID)
if cs.logger != nil {
cs.logger.LogDebug("collections", "Unloaded collections for character %d", characterID)
}
return nil
}
// ProcessItemFound processes when a player finds an item
func (cs *CollectionService) ProcessItemFound(characterID, itemID int32) error {
cs.mu.RLock()
playerList, exists := cs.playerLists[characterID]
cs.mu.RUnlock()
if !exists {
return fmt.Errorf("player collections not loaded for character %d", characterID)
}
updatedCollections, err := cs.manager.ProcessItemFound(playerList, itemID)
if err != nil {
return fmt.Errorf("failed to process found item: %w", err)
}
// Notify client and event handler of updates
for _, collection := range updatedCollections {
if cs.clientManager != nil {
cs.clientManager.SendCollectionUpdate(characterID, collection)
}
if cs.eventHandler != nil {
cs.eventHandler.OnItemFound(characterID, collection.GetID(), itemID)
}
if cs.logger != nil {
cs.logger.LogDebug("collections", "Character %d found item %d for collection %d (%s)",
characterID, itemID, collection.GetID(), collection.GetName())
}
}
return nil
}
// CompleteCollection processes collection completion
func (cs *CollectionService) CompleteCollection(characterID, collectionID int32, rewardProvider RewardProvider) error {
cs.mu.RLock()
playerList, exists := cs.playerLists[characterID]
cs.mu.RUnlock()
if !exists {
return fmt.Errorf("player collections not loaded for character %d", characterID)
}
collection := playerList.GetCollection(collectionID)
if collection == nil {
return fmt.Errorf("collection %d not found for character %d", collectionID, characterID)
}
// Complete the collection
if err := cs.manager.CompleteCollection(playerList, collectionID, rewardProvider); err != nil {
return fmt.Errorf("failed to complete collection: %w", err)
}
// Notify client and event handler
if cs.clientManager != nil {
cs.clientManager.SendCollectionComplete(characterID, collection)
}
if cs.eventHandler != nil {
rewards := collection.GetRewardItems()
selectableRewards := collection.GetSelectableRewardItems()
allRewards := append(rewards, selectableRewards...)
cs.eventHandler.OnCollectionCompleted(characterID, collectionID)
cs.eventHandler.OnRewardClaimed(characterID, collectionID, allRewards,
collection.GetRewardCoin(), collection.GetRewardXP())
}
if cs.logger != nil {
cs.logger.LogInfo("collections", "Character %d completed collection %d (%s)",
characterID, collectionID, collection.GetName())
}
return nil
}
// GetPlayerCollections returns all collections for a player
func (cs *CollectionService) GetPlayerCollections(characterID int32) ([]*Collection, error) {
cs.mu.RLock()
playerList, exists := cs.playerLists[characterID]
cs.mu.RUnlock()
if !exists {
return nil, fmt.Errorf("player collections not loaded for character %d", characterID)
}
return playerList.GetAllCollections(), nil
}
// GetPlayerCollectionProgress returns detailed progress for all player collections
func (cs *CollectionService) GetPlayerCollectionProgress(characterID int32) ([]CollectionProgress, error) {
cs.mu.RLock()
playerList, exists := cs.playerLists[characterID]
cs.mu.RUnlock()
if !exists {
return nil, fmt.Errorf("player collections not loaded for character %d", characterID)
}
return playerList.GetCollectionProgress(), nil
}
// SendCollectionList sends available collections to a player
func (cs *CollectionService) SendCollectionList(characterID int32, playerLevel int8) error {
if cs.clientManager == nil {
return fmt.Errorf("client manager not available")
}
collections := cs.manager.GetAvailableCollections(playerLevel)
collectionInfos := make([]CollectionInfo, len(collections))
for i, collection := range collections {
collectionInfos[i] = collection.GetCollectionInfo()
}
return cs.clientManager.SendCollectionList(characterID, collectionInfos)
}
// SaveAllPlayerCollections saves all loaded player collections
func (cs *CollectionService) SaveAllPlayerCollections(ctx context.Context) error {
cs.mu.RLock()
playerLists := make(map[int32]*PlayerCollectionList)
for characterID, playerList := range cs.playerLists {
playerLists[characterID] = playerList
}
cs.mu.RUnlock()
var saveErrors []error
for characterID, playerList := range playerLists {
if err := playerList.SaveCollections(ctx); err != nil {
saveErrors = append(saveErrors, fmt.Errorf("character %d: %w", characterID, err))
if cs.logger != nil {
cs.logger.LogError("collections", "Failed to save collections for character %d: %v", characterID, err)
}
}
}
if len(saveErrors) > 0 {
return fmt.Errorf("failed to save some player collections: %v", saveErrors)
}
return nil
}
// GetLoadedPlayerCount returns the number of players with loaded collections
func (cs *CollectionService) GetLoadedPlayerCount() int {
cs.mu.RLock()
defer cs.mu.RUnlock()
return len(cs.playerLists)
}
// IsPlayerLoaded checks if a player's collections are loaded
func (cs *CollectionService) IsPlayerLoaded(characterID int32) bool {
cs.mu.RLock()
defer cs.mu.RUnlock()
_, exists := cs.playerLists[characterID]
return exists
}
// GetMasterCollection returns a collection from the master list
func (cs *CollectionService) GetMasterCollection(collectionID int32) *Collection {
return cs.manager.GetCollection(collectionID)
}
// GetAllCategories returns all collection categories
func (cs *CollectionService) GetAllCategories() []string {
return cs.manager.GetAllCategories()
}
// SearchCollections searches for collections by name
func (cs *CollectionService) SearchCollections(searchTerm string) []*Collection {
return cs.manager.SearchCollections(searchTerm)
}