380 lines
12 KiB
Go
380 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)
|
|
} |