eq2go/internal/quests/interfaces.go
2025-08-05 20:41:20 -05:00

479 lines
14 KiB
Go

package quests
import "fmt"
// Player interface defines the required player functionality for quest system
type Player interface {
// Basic player information
GetID() int32
GetName() string
GetLevel() int8
GetTSLevel() int8
GetClass() int8
GetTSClass() int8
GetRace() int8
// Client interface for packet sending
GetClient() Client
// Quest-related player methods
GetQuest(questID int32) *Quest
HasQuest(questID int32) bool
HasQuestBeenCompleted(questID int32) bool
AddQuest(quest *Quest) error
RemoveQuest(questID int32) bool
// Position and zone information
GetX() float32
GetY() float32
GetZ() float32
GetZoneID() int32
// Faction information
GetFactionValue(factionID int32) int32
// Experience and rewards
AddCoins(amount int64)
AddExp(amount int32)
AddTSExp(amount int32)
AddStatusPoints(amount int32)
// Inventory management
GetPlayerItemList() ItemList
// Arrow color calculation (difficulty indication)
GetArrowColor(level int8) int8
GetTSArrowColor(level int8) int8
}
// Client interface defines client functionality needed for quest system
type Client interface {
// Basic client information
GetVersion() int16
GetNameCRC() int32
GetPlayer() Player
// Packet sending
QueuePacket(packet Packet)
SimpleMessage(color int32, message string)
// Quest-specific client methods
SendQuestJournalUpdate(quest *Quest, forceUpdate bool)
PopulateQuestRewardItems(items *[]Item, packet PacketStruct, arrayName ...string)
}
// Spawn interface defines spawn functionality needed for quest system
type Spawn interface {
// Basic spawn information
GetID() int32
GetDatabaseID() int32
GetName() string
GetRace() int8
GetModelType() int16
// Position information
GetX() float32
GetY() float32
GetZ() float32
GetZoneID() int32
}
// Spell interface defines spell functionality needed for quest system
type Spell interface {
GetSpellID() int32
GetName() string
}
// Item interface defines item functionality needed for quest system
type Item interface {
GetItemID() int32
GetName() string
GetIcon(version int16) int32
GetDetails() ItemDetails
GetStackCount() int32
}
// ItemDetails interface defines item detail functionality
type ItemDetails interface {
GetItemID() int32
GetUniqueID() int32
GetCount() int32
}
// ItemList interface defines item list functionality
type ItemList interface {
GetItemFromID(itemID int32, count int32) Item
}
// Packet interface defines packet functionality
type Packet interface {
// Packet data methods (placeholder)
Serialize() []byte
}
// PacketStruct interface defines packet structure functionality
type PacketStruct interface {
// Packet building methods
SetDataByName(name string, value any, index ...int)
SetArrayLengthByName(name string, length int)
SetArrayDataByName(name string, value any, index int)
SetSubArrayLengthByName(name string, length int, index int)
SetSubArrayDataByName(name string, value any, index1, index2 int)
SetSubstructArrayDataByName(substruct, name string, value any, index int)
SetItemArrayDataByName(name string, item Item, player Player, index int, flag ...int)
Serialize() Packet
GetVersion() int16
}
// QuestOfferer interface defines NPCs that can offer quests
type QuestOfferer interface {
Spawn
GetOfferedQuests() []int32
CanOfferQuest(questID int32, player Player) bool
OfferQuest(questID int32, player Player) error
}
// QuestReceiver interface defines NPCs that can receive completed quests
type QuestReceiver interface {
Spawn
GetAcceptedQuests() []int32
CanAcceptQuest(questID int32, player Player) bool
AcceptCompletedQuest(questID int32, player Player) error
}
// QuestValidator interface defines quest validation functionality
type QuestValidator interface {
ValidatePrerequisites(quest *Quest, player Player) error
ValidateQuestCompletion(quest *Quest, player Player) error
ValidateQuestSharing(quest *Quest, sharer, receiver Player) error
}
// QuestRewardProcessor interface defines quest reward processing
type QuestRewardProcessor interface {
ProcessQuestRewards(quest *Quest, player Player) error
CalculateRewards(quest *Quest, player Player) *QuestRewards
GiveRewards(rewards *QuestRewards, player Player) error
}
// QuestRewards contains calculated quest rewards
type QuestRewards struct {
Coins int64 `json:"coins"`
Experience int32 `json:"experience"`
TSExperience int32 `json:"ts_experience"`
StatusPoints int32 `json:"status_points"`
Items []Item `json:"items"`
FactionRewards map[int32]int32 `json:"faction_rewards"`
}
// QuestEventHandler interface defines quest event handling
type QuestEventHandler interface {
OnQuestStarted(quest *Quest, player Player)
OnQuestStepCompleted(quest *Quest, step *QuestStep, player Player)
OnQuestCompleted(quest *Quest, player Player)
OnQuestFailed(quest *Quest, step *QuestStep, player Player)
OnQuestAbandoned(quest *Quest, player Player)
OnQuestShared(quest *Quest, sharer, receiver Player)
}
// Database interface defines database operations for quest system
type Database interface {
// Quest CRUD operations
LoadQuest(questID int32) (*Quest, error)
SaveQuest(quest *Quest) error
DeleteQuest(questID int32) error
LoadAllQuests() ([]*Quest, error)
// Player quest operations
LoadPlayerQuests(playerID int32) ([]*Quest, error)
SavePlayerQuest(playerID int32, quest *Quest) error
DeletePlayerQuest(playerID, questID int32) error
MarkQuestCompleted(playerID, questID int32) error
IsQuestCompleted(playerID, questID int32) bool
// Quest step operations
SaveQuestStepProgress(playerID, questID, stepID int32, progress int32) error
LoadQuestStepProgress(playerID, questID, stepID int32) (int32, error)
}
// Logger interface defines logging functionality for quest system
type Logger interface {
LogInfo(message string, args ...any)
LogError(message string, args ...any)
LogDebug(message string, args ...any)
LogWarning(message string, args ...any)
}
// Configuration interface defines quest system configuration
type Configuration interface {
GetMaxPlayerQuests() int
GetQuestSharingEnabled() bool
GetQuestFailureEnabled() bool
GetQuestTimersEnabled() bool
GetXPBonusMultiplier() float64
GetCoinBonusMultiplier() float64
GetStatusBonusMultiplier() float64
}
// QuestSystemAdapter provides integration with other game systems
type QuestSystemAdapter struct {
questManager *QuestManager
validator QuestValidator
rewardProcessor QuestRewardProcessor
eventHandler QuestEventHandler
database Database
logger Logger
config Configuration
}
// NewQuestSystemAdapter creates a new quest system adapter
func NewQuestSystemAdapter(questManager *QuestManager, validator QuestValidator, rewardProcessor QuestRewardProcessor, eventHandler QuestEventHandler, database Database, logger Logger, config Configuration) *QuestSystemAdapter {
return &QuestSystemAdapter{
questManager: questManager,
validator: validator,
rewardProcessor: rewardProcessor,
eventHandler: eventHandler,
database: database,
logger: logger,
config: config,
}
}
// StartQuest handles starting a new quest for a player
func (qsa *QuestSystemAdapter) StartQuest(questID int32, player Player) error {
// Get quest from master list
quest := qsa.questManager.GetMasterList().GetQuest(questID, true)
if quest == nil {
return fmt.Errorf("quest %d not found", questID)
}
// Validate prerequisites
if qsa.validator != nil {
if err := qsa.validator.ValidatePrerequisites(quest, player); err != nil {
return fmt.Errorf("quest prerequisites not met: %w", err)
}
}
// Check if player can accept more quests
if qsa.config != nil {
maxQuests := qsa.config.GetMaxPlayerQuests()
if maxQuests > 0 && qsa.questManager.GetPlayerQuestCount(player.GetID()) >= maxQuests {
return fmt.Errorf("player has too many active quests")
}
}
// Give quest to player
_, err := qsa.questManager.GiveQuestToPlayer(player.GetID(), questID)
if err != nil {
return err
}
// Save to database
if qsa.database != nil {
if err := qsa.database.SavePlayerQuest(player.GetID(), quest); err != nil {
qsa.logger.LogError("Failed to save player quest to database: %v", err)
}
}
// Trigger event
if qsa.eventHandler != nil {
qsa.eventHandler.OnQuestStarted(quest, player)
}
// Log
if qsa.logger != nil {
qsa.logger.LogInfo("Player %s (%d) started quest %s (%d)", player.GetName(), player.GetID(), quest.Name, questID)
}
return nil
}
// CompleteQuest handles completing a quest for a player
func (qsa *QuestSystemAdapter) CompleteQuest(questID int32, player Player) error {
// Get player's quest
quest := qsa.questManager.GetPlayerQuest(player.GetID(), questID)
if quest == nil {
return fmt.Errorf("player does not have quest %d", questID)
}
// Validate completion
if qsa.validator != nil {
if err := qsa.validator.ValidateQuestCompletion(quest, player); err != nil {
return fmt.Errorf("quest completion validation failed: %w", err)
}
}
// Check if quest is actually complete
if !quest.GetCompleted() {
return fmt.Errorf("quest is not complete")
}
// Process rewards
if qsa.rewardProcessor != nil {
if err := qsa.rewardProcessor.ProcessQuestRewards(quest, player); err != nil {
qsa.logger.LogError("Failed to process quest rewards: %v", err)
return fmt.Errorf("failed to process quest rewards: %w", err)
}
}
// Mark as completed in database
if qsa.database != nil {
if err := qsa.database.MarkQuestCompleted(player.GetID(), questID); err != nil {
qsa.logger.LogError("Failed to mark quest as completed in database: %v", err)
}
}
// Remove from active quests if not repeatable
if !quest.Repeatable {
qsa.questManager.RemoveQuestFromPlayer(player.GetID(), questID)
}
// Trigger event
if qsa.eventHandler != nil {
qsa.eventHandler.OnQuestCompleted(quest, player)
}
// Log
if qsa.logger != nil {
qsa.logger.LogInfo("Player %s (%d) completed quest %s (%d)", player.GetName(), player.GetID(), quest.Name, questID)
}
return nil
}
// UpdateQuestProgress handles updating quest step progress
func (qsa *QuestSystemAdapter) UpdateQuestProgress(questID, stepID, progress int32, player Player) error {
// Get player's quest
quest := qsa.questManager.GetPlayerQuest(player.GetID(), questID)
if quest == nil {
return fmt.Errorf("player does not have quest %d", questID)
}
// Update progress
if !quest.AddStepProgress(stepID, progress) {
return fmt.Errorf("failed to update quest step progress")
}
// Check if step is now complete
step := quest.GetQuestStep(stepID)
if step != nil && step.Complete() {
// Execute complete action if exists
if quest.HasCompleteAction(stepID) {
if err := quest.ExecuteCompleteAction(stepID); err != nil {
qsa.logger.LogError("Failed to execute quest step complete action: %v", err)
}
}
// Trigger event
if qsa.eventHandler != nil {
qsa.eventHandler.OnQuestStepCompleted(quest, step, player)
}
// Log
if qsa.logger != nil {
qsa.logger.LogInfo("Player %s (%d) completed step %d of quest %s (%d)", player.GetName(), player.GetID(), stepID, quest.Name, questID)
}
}
// Save progress to database
if qsa.database != nil {
if err := qsa.database.SaveQuestStepProgress(player.GetID(), questID, stepID, step.GetStepProgress()); err != nil {
qsa.logger.LogError("Failed to save quest step progress to database: %v", err)
}
}
return nil
}
// AbandonQuest handles abandoning a quest
func (qsa *QuestSystemAdapter) AbandonQuest(questID int32, player Player) error {
// Get player's quest
quest := qsa.questManager.GetPlayerQuest(player.GetID(), questID)
if quest == nil {
return fmt.Errorf("player does not have quest %d", questID)
}
// Check if quest can be abandoned
if !quest.CanDeleteQuest {
return fmt.Errorf("quest cannot be abandoned")
}
// Remove from player
if !qsa.questManager.RemoveQuestFromPlayer(player.GetID(), questID) {
return fmt.Errorf("failed to remove quest from player")
}
// Remove from database
if qsa.database != nil {
if err := qsa.database.DeletePlayerQuest(player.GetID(), questID); err != nil {
qsa.logger.LogError("Failed to delete player quest from database: %v", err)
}
}
// Trigger event
if qsa.eventHandler != nil {
qsa.eventHandler.OnQuestAbandoned(quest, player)
}
// Log
if qsa.logger != nil {
qsa.logger.LogInfo("Player %s (%d) abandoned quest %s (%d)", player.GetName(), player.GetID(), quest.Name, questID)
}
return nil
}
// ShareQuest handles sharing a quest between players
func (qsa *QuestSystemAdapter) ShareQuest(questID int32, sharer, receiver Player) error {
if !qsa.config.GetQuestSharingEnabled() {
return fmt.Errorf("quest sharing is disabled")
}
// Get sharer's quest
quest := qsa.questManager.GetPlayerQuest(sharer.GetID(), questID)
if quest == nil {
return fmt.Errorf("sharer does not have quest %d", questID)
}
// Validate sharing
if qsa.validator != nil {
if err := qsa.validator.ValidateQuestSharing(quest, sharer, receiver); err != nil {
return fmt.Errorf("quest sharing validation failed: %w", err)
}
}
// Check if receiver can accept more quests
if qsa.config != nil {
maxQuests := qsa.config.GetMaxPlayerQuests()
if maxQuests > 0 && qsa.questManager.GetPlayerQuestCount(receiver.GetID()) >= maxQuests {
return fmt.Errorf("receiver has too many active quests")
}
}
// Give quest to receiver
_, err := qsa.questManager.GiveQuestToPlayer(receiver.GetID(), questID)
if err != nil {
return fmt.Errorf("failed to give quest to receiver: %w", err)
}
// Save to database
if qsa.database != nil {
receiverQuest := qsa.questManager.GetPlayerQuest(receiver.GetID(), questID)
if err := qsa.database.SavePlayerQuest(receiver.GetID(), receiverQuest); err != nil {
qsa.logger.LogError("Failed to save shared quest to database: %v", err)
}
}
// Trigger event
if qsa.eventHandler != nil {
qsa.eventHandler.OnQuestShared(quest, sharer, receiver)
}
// Log
if qsa.logger != nil {
qsa.logger.LogInfo("Player %s (%d) shared quest %s (%d) with %s (%d)",
sharer.GetName(), sharer.GetID(), quest.Name, questID, receiver.GetName(), receiver.GetID())
}
return nil
}