eq2go/internal/quests/manager.go

451 lines
11 KiB
Go

package quests
import (
"fmt"
"sync"
)
// MasterQuestList manages all quests in the system
type MasterQuestList struct {
quests map[int32]*Quest
mutex sync.RWMutex
}
// NewMasterQuestList creates a new master quest list
func NewMasterQuestList() *MasterQuestList {
return &MasterQuestList{
quests: make(map[int32]*Quest),
}
}
// AddQuest adds a quest to the master list
func (mql *MasterQuestList) AddQuest(questID int32, quest *Quest) error {
if quest == nil {
return fmt.Errorf("quest cannot be nil")
}
if questID != quest.ID {
return fmt.Errorf("quest ID mismatch: provided %d, quest has %d", questID, quest.ID)
}
mql.mutex.Lock()
defer mql.mutex.Unlock()
if _, exists := mql.quests[questID]; exists {
return fmt.Errorf("quest %d already exists", questID)
}
mql.quests[questID] = quest
return nil
}
// GetQuest returns a quest by ID, optionally creating a copy
func (mql *MasterQuestList) GetQuest(questID int32, copyQuest bool) *Quest {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
quest, exists := mql.quests[questID]
if !exists {
return nil
}
if copyQuest {
return quest.Copy()
}
return quest
}
// RemoveQuest removes a quest from the master list
func (mql *MasterQuestList) RemoveQuest(questID int32) bool {
mql.mutex.Lock()
defer mql.mutex.Unlock()
if _, exists := mql.quests[questID]; exists {
delete(mql.quests, questID)
return true
}
return false
}
// HasQuest checks if a quest exists
func (mql *MasterQuestList) HasQuest(questID int32) bool {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
_, exists := mql.quests[questID]
return exists
}
// GetAllQuests returns a copy of all quests map
func (mql *MasterQuestList) GetAllQuests() map[int32]*Quest {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
quests := make(map[int32]*Quest)
for id, quest := range mql.quests {
quests[id] = quest
}
return quests
}
// GetQuestsByLevel returns quests within a level range
func (mql *MasterQuestList) GetQuestsByLevel(minLevel, maxLevel int8) []*Quest {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
var quests []*Quest
for _, quest := range mql.quests {
if quest.Level >= minLevel && quest.Level <= maxLevel {
quests = append(quests, quest)
}
}
return quests
}
// GetQuestsByType returns quests of a specific type
func (mql *MasterQuestList) GetQuestsByType(questType string) []*Quest {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
var quests []*Quest
for _, quest := range mql.quests {
if quest.Type == questType {
quests = append(quests, quest)
}
}
return quests
}
// GetQuestsByZone returns quests in a specific zone
func (mql *MasterQuestList) GetQuestsByZone(zone string) []*Quest {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
var quests []*Quest
for _, quest := range mql.quests {
if quest.Zone == zone {
quests = append(quests, quest)
}
}
return quests
}
// GetQuestsByGiver returns quests given by a specific NPC
func (mql *MasterQuestList) GetQuestsByGiver(giverID int32) []*Quest {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
var quests []*Quest
for _, quest := range mql.quests {
if quest.QuestGiver == giverID {
quests = append(quests, quest)
}
}
return quests
}
// GetRepeatableQuests returns all repeatable quests
func (mql *MasterQuestList) GetRepeatableQuests() []*Quest {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
var quests []*Quest
for _, quest := range mql.quests {
if quest.Repeatable {
quests = append(quests, quest)
}
}
return quests
}
// GetQuestCount returns the total number of quests
func (mql *MasterQuestList) GetQuestCount() int {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
return len(mql.quests)
}
// Clear removes all quests
func (mql *MasterQuestList) Clear() {
mql.mutex.Lock()
defer mql.mutex.Unlock()
mql.quests = make(map[int32]*Quest)
}
// Reload clears and reloads all quests
func (mql *MasterQuestList) Reload() {
mql.Clear()
// TODO: Implement quest reloading from database or files
// This would typically involve:
// 1. Loading quest data from database
// 2. Creating Quest objects
// 3. Adding them to the master list
// For now, this is a placeholder
fmt.Println("Quest reload requested - implementation pending")
}
// ValidateAllQuests validates all quests in the master list
func (mql *MasterQuestList) ValidateAllQuests() []error {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
var errors []error
for questID, quest := range mql.quests {
if err := quest.ValidateQuest(); err != nil {
errors = append(errors, fmt.Errorf("quest %d validation failed: %w", questID, err))
}
}
return errors
}
// GetQuestStatistics returns basic statistics about the quest system
func (mql *MasterQuestList) GetQuestStatistics() *QuestStatistics {
mql.mutex.RLock()
defer mql.mutex.RUnlock()
stats := &QuestStatistics{
TotalQuests: len(mql.quests),
QuestsByType: make(map[string]int),
QuestsByLevel: make(map[int8]int),
RepeatableCount: 0,
HiddenCount: 0,
}
for _, quest := range mql.quests {
// Count by type
stats.QuestsByType[quest.Type]++
// Count by level
stats.QuestsByLevel[quest.Level]++
// Count special flags
if quest.Repeatable {
stats.RepeatableCount++
}
if quest.Hidden {
stats.HiddenCount++
}
}
return stats
}
// QuestStatistics contains statistical information about quests
type QuestStatistics struct {
TotalQuests int `json:"total_quests"`
QuestsByType map[string]int `json:"quests_by_type"`
QuestsByLevel map[int8]int `json:"quests_by_level"`
RepeatableCount int `json:"repeatable_count"`
HiddenCount int `json:"hidden_count"`
}
// QuestManager provides high-level quest management functionality
type QuestManager struct {
masterList *MasterQuestList
playerQuests map[int32]map[int32]*Quest // playerID -> questID -> quest
mutex sync.RWMutex
}
// NewQuestManager creates a new quest manager
func NewQuestManager() *QuestManager {
return &QuestManager{
masterList: NewMasterQuestList(),
playerQuests: make(map[int32]map[int32]*Quest),
}
}
// GetMasterList returns the master quest list
func (qm *QuestManager) GetMasterList() *MasterQuestList {
return qm.masterList
}
// GiveQuestToPlayer assigns a quest to a player
func (qm *QuestManager) GiveQuestToPlayer(playerID, questID int32) (*Quest, error) {
// Get quest from master list (copy it)
quest := qm.masterList.GetQuest(questID, true)
if quest == nil {
return nil, fmt.Errorf("quest %d not found", questID)
}
qm.mutex.Lock()
defer qm.mutex.Unlock()
// Initialize player quest map if needed
if qm.playerQuests[playerID] == nil {
qm.playerQuests[playerID] = make(map[int32]*Quest)
}
// Check if player already has this quest
if _, exists := qm.playerQuests[playerID][questID]; exists {
return nil, fmt.Errorf("player %d already has quest %d", playerID, questID)
}
// Assign quest to player
qm.playerQuests[playerID][questID] = quest
return quest, nil
}
// RemoveQuestFromPlayer removes a quest from a player
func (qm *QuestManager) RemoveQuestFromPlayer(playerID, questID int32) bool {
qm.mutex.Lock()
defer qm.mutex.Unlock()
if playerQuests, exists := qm.playerQuests[playerID]; exists {
if _, questExists := playerQuests[questID]; questExists {
delete(playerQuests, questID)
// Clean up empty player quest map
if len(playerQuests) == 0 {
delete(qm.playerQuests, playerID)
}
return true
}
}
return false
}
// GetPlayerQuest returns a specific quest for a player
func (qm *QuestManager) GetPlayerQuest(playerID, questID int32) *Quest {
qm.mutex.RLock()
defer qm.mutex.RUnlock()
if playerQuests, exists := qm.playerQuests[playerID]; exists {
return playerQuests[questID]
}
return nil
}
// GetPlayerQuests returns all quests for a player
func (qm *QuestManager) GetPlayerQuests(playerID int32) map[int32]*Quest {
qm.mutex.RLock()
defer qm.mutex.RUnlock()
if playerQuests, exists := qm.playerQuests[playerID]; exists {
// Return a copy
quests := make(map[int32]*Quest)
for questID, quest := range playerQuests {
quests[questID] = quest
}
return quests
}
return make(map[int32]*Quest)
}
// PlayerHasQuest checks if a player has a specific quest
func (qm *QuestManager) PlayerHasQuest(playerID, questID int32) bool {
return qm.GetPlayerQuest(playerID, questID) != nil
}
// GetPlayerQuestCount returns the number of quests a player has
func (qm *QuestManager) GetPlayerQuestCount(playerID int32) int {
qm.mutex.RLock()
defer qm.mutex.RUnlock()
if playerQuests, exists := qm.playerQuests[playerID]; exists {
return len(playerQuests)
}
return 0
}
// ClearPlayerQuests removes all quests from a player
func (qm *QuestManager) ClearPlayerQuests(playerID int32) {
qm.mutex.Lock()
defer qm.mutex.Unlock()
delete(qm.playerQuests, playerID)
}
// GetAllPlayerIDs returns all player IDs that have quests
func (qm *QuestManager) GetAllPlayerIDs() []int32 {
qm.mutex.RLock()
defer qm.mutex.RUnlock()
var playerIDs []int32
for playerID := range qm.playerQuests {
playerIDs = append(playerIDs, playerID)
}
return playerIDs
}
// UpdatePlayerQuestProgress updates progress for a player's quest step
func (qm *QuestManager) UpdatePlayerQuestProgress(playerID, questID, stepID, progress int32) bool {
quest := qm.GetPlayerQuest(playerID, questID)
if quest == nil {
return false
}
return quest.AddStepProgress(stepID, progress)
}
// CompletePlayerQuestStep marks a quest step as complete for a player
func (qm *QuestManager) CompletePlayerQuestStep(playerID, questID, stepID int32) bool {
quest := qm.GetPlayerQuest(playerID, questID)
if quest == nil {
return false
}
return quest.SetStepComplete(stepID)
}
// IsPlayerQuestComplete checks if a player's quest is complete
func (qm *QuestManager) IsPlayerQuestComplete(playerID, questID int32) bool {
quest := qm.GetPlayerQuest(playerID, questID)
if quest == nil {
return false
}
return quest.GetCompleted()
}
// GetPlayerQuestStatistics returns statistics for a player's quests
func (qm *QuestManager) GetPlayerQuestStatistics(playerID int32) *PlayerQuestStatistics {
quests := qm.GetPlayerQuests(playerID)
stats := &PlayerQuestStatistics{
TotalQuests: len(quests),
CompletedQuests: 0,
QuestsByType: make(map[string]int),
QuestsByLevel: make(map[int8]int),
}
for _, quest := range quests {
if quest.GetCompleted() {
stats.CompletedQuests++
}
stats.QuestsByType[quest.Type]++
stats.QuestsByLevel[quest.Level]++
}
return stats
}
// PlayerQuestStatistics contains statistical information about a player's quests
type PlayerQuestStatistics struct {
TotalQuests int `json:"total_quests"`
CompletedQuests int `json:"completed_quests"`
QuestsByType map[string]int `json:"quests_by_type"`
QuestsByLevel map[int8]int `json:"quests_by_level"`
}