879 lines
23 KiB
Go
879 lines
23 KiB
Go
package player
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"eq2emu/internal/common"
|
|
"eq2emu/internal/entity"
|
|
"eq2emu/internal/quests"
|
|
"eq2emu/internal/spawn"
|
|
)
|
|
|
|
// Global XP table
|
|
var levelXPReq map[int8]int32
|
|
var xpTableOnce sync.Once
|
|
|
|
// NewPlayer creates a new player instance
|
|
func NewPlayer() *Player {
|
|
p := &Player{
|
|
charID: 0,
|
|
spawnID: 1,
|
|
spawnIndex: 1,
|
|
tutorialStep: 0,
|
|
packetNum: 0,
|
|
posPacketSpeed: 0,
|
|
houseVaultSlots: 0,
|
|
|
|
// Initialize maps
|
|
playerQuests: make(map[int32]*quests.Quest),
|
|
completedQuests: make(map[int32]*quests.Quest),
|
|
pendingQuests: make(map[int32]*quests.Quest),
|
|
currentQuestFlagged: make(map[*spawn.Spawn]bool),
|
|
playerSpawnQuestsRequired: make(map[int32][]int32),
|
|
playerSpawnHistoryRequired: make(map[int32][]int32),
|
|
spawnVisPacketList: make(map[int32]string),
|
|
spawnInfoPacketList: make(map[int32]string),
|
|
spawnPosPacketList: make(map[int32]string),
|
|
spawnPacketSent: make(map[int32]int8),
|
|
spawnStateList: make(map[int32]*SpawnQueueState),
|
|
playerSpawnIDMap: make(map[int32]*spawn.Spawn),
|
|
playerSpawnReverseIDMap: make(map[*spawn.Spawn]int32),
|
|
playerAggroRangeSpawns: make(map[int32]bool),
|
|
pendingLootItems: make(map[int32]map[int32]bool),
|
|
friendList: make(map[string]int8),
|
|
ignoreList: make(map[string]int8),
|
|
characterHistory: make(map[int8]map[int8][]*HistoryData),
|
|
charLuaHistory: make(map[int32]*LUAHistory),
|
|
playersPoiList: make(map[int32][]int32),
|
|
pendingSelectableItemRewards: make(map[int32][]Item),
|
|
statistics: make(map[int32]*Statistic),
|
|
mailList: make(map[int32]*Mail),
|
|
targetInvisHistory: make(map[int32]bool),
|
|
spawnedBots: make(map[int32]int32),
|
|
// macroIcons field removed - not in struct definition
|
|
sortedTraitList: make(map[int8]map[int8][]*TraitData),
|
|
classTraining: make(map[int8][]*TraitData),
|
|
raceTraits: make(map[int8][]*TraitData),
|
|
innateRaceTraits: make(map[int8][]*TraitData),
|
|
focusEffects: make(map[int8][]*TraitData),
|
|
}
|
|
|
|
// Initialize entity base
|
|
p.Entity = *entity.NewEntity()
|
|
|
|
// Set player-specific defaults
|
|
p.SetSpawnType(4) // Player spawn type
|
|
// TODO: Set appearance data through proper methods when available
|
|
// appearance.DisplayName = 1
|
|
// appearance.ShowCommandIcon = 1
|
|
// appearance.PlayerFlag = 1
|
|
// appearance.Targetable = 1
|
|
// appearance.ShowLevel = 1
|
|
|
|
// Set default away message
|
|
p.awayMessage = "Sorry, I am A.F.K. (Away From Keyboard)"
|
|
|
|
// Add player-specific commands
|
|
p.AddSecondaryEntityCommand("Inspect", 10000, "inspect_player", "", 0, 0)
|
|
p.AddSecondaryEntityCommand("Who", 10000, "who", "", 0, 0)
|
|
|
|
// Initialize self in spawn maps
|
|
p.playerSpawnIDMap[1] = p.Entity.Spawn
|
|
p.playerSpawnReverseIDMap[p.Entity.Spawn] = 1
|
|
|
|
// Set save spell effects
|
|
p.stopSaveSpellEffects = false
|
|
|
|
// Initialize trait update flag
|
|
p.needTraitUpdate.Store(true)
|
|
|
|
// Initialize control flags
|
|
p.controlFlags = PlayerControlFlags{
|
|
flagChanges: make(map[int8]map[int8]int8),
|
|
currentFlags: make(map[int8]map[int8]bool),
|
|
}
|
|
|
|
// Initialize character instances
|
|
p.characterInstances = CharacterInstances{
|
|
instanceList: make([]InstanceData, 0),
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
// GetClient returns the player's client connection
|
|
func (p *Player) GetClient() *Client {
|
|
return p.client
|
|
}
|
|
|
|
// SetClient sets the player's client connection
|
|
func (p *Player) SetClient(client *Client) {
|
|
p.client = client
|
|
}
|
|
|
|
// GetPlayerInfo returns the player's info structure, creating it if needed
|
|
func (p *Player) GetPlayerInfo() *PlayerInfo {
|
|
if p.info == nil {
|
|
p.info = NewPlayerInfo(p)
|
|
}
|
|
return p.info
|
|
}
|
|
|
|
// IsPlayer always returns true for Player type
|
|
func (p *Player) IsPlayer() bool {
|
|
return true
|
|
}
|
|
|
|
// GetCharacterID returns the character's database ID
|
|
func (p *Player) GetCharacterID() int32 {
|
|
return p.charID
|
|
}
|
|
|
|
// SetCharacterID sets the character's database ID
|
|
func (p *Player) SetCharacterID(id int32) {
|
|
p.charID = id
|
|
}
|
|
|
|
// GetTutorialStep returns the current tutorial step
|
|
func (p *Player) GetTutorialStep() int8 {
|
|
return p.tutorialStep
|
|
}
|
|
|
|
// SetTutorialStep sets the current tutorial step
|
|
func (p *Player) SetTutorialStep(step int8) {
|
|
p.tutorialStep = step
|
|
}
|
|
|
|
// SetCharSheetChanged marks the character sheet as needing an update
|
|
func (p *Player) SetCharSheetChanged(val bool) {
|
|
p.charsheetChanged.Store(val)
|
|
}
|
|
|
|
// GetCharSheetChanged returns whether the character sheet needs updating
|
|
func (p *Player) GetCharSheetChanged() bool {
|
|
return p.charsheetChanged.Load()
|
|
}
|
|
|
|
// SetRaidSheetChanged marks the raid sheet as needing an update
|
|
func (p *Player) SetRaidSheetChanged(val bool) {
|
|
p.raidsheetChanged.Store(val)
|
|
}
|
|
|
|
// GetRaidSheetChanged returns whether the raid sheet needs updating
|
|
func (p *Player) GetRaidSheetChanged() bool {
|
|
return p.raidsheetChanged.Load()
|
|
}
|
|
|
|
// AddFriend adds a friend to the player's friend list
|
|
func (p *Player) AddFriend(name string, save bool) {
|
|
p.friendList[name] = 1
|
|
if save {
|
|
// TODO: Save to database
|
|
}
|
|
}
|
|
|
|
// IsFriend checks if a name is in the friend list
|
|
func (p *Player) IsFriend(name string) bool {
|
|
_, exists := p.friendList[name]
|
|
return exists
|
|
}
|
|
|
|
// RemoveFriend removes a friend from the friend list
|
|
func (p *Player) RemoveFriend(name string) {
|
|
delete(p.friendList, name)
|
|
// TODO: Remove from database
|
|
}
|
|
|
|
// GetFriends returns the friend list
|
|
func (p *Player) GetFriends() map[string]int8 {
|
|
return p.friendList
|
|
}
|
|
|
|
// AddIgnore adds a player to the ignore list
|
|
func (p *Player) AddIgnore(name string, save bool) {
|
|
p.ignoreList[name] = 1
|
|
if save {
|
|
// TODO: Save to database
|
|
}
|
|
}
|
|
|
|
// IsIgnored checks if a player is ignored
|
|
func (p *Player) IsIgnored(name string) bool {
|
|
_, exists := p.ignoreList[name]
|
|
return exists
|
|
}
|
|
|
|
// RemoveIgnore removes a player from the ignore list
|
|
func (p *Player) RemoveIgnore(name string) {
|
|
delete(p.ignoreList, name)
|
|
// TODO: Remove from database
|
|
}
|
|
|
|
// GetIgnoredPlayers returns the ignore list
|
|
func (p *Player) GetIgnoredPlayers() map[string]int8 {
|
|
return p.ignoreList
|
|
}
|
|
|
|
// GetPlayerDiscoveredPOIs returns the player's discovered POIs
|
|
func (p *Player) GetPlayerDiscoveredPOIs() map[int32][]int32 {
|
|
return p.playersPoiList
|
|
}
|
|
|
|
// AddPlayerDiscoveredPOI adds a discovered POI for the player
|
|
func (p *Player) AddPlayerDiscoveredPOI(locationID int32) {
|
|
// TODO: Implement POI discovery logic
|
|
if p.playersPoiList[locationID] == nil {
|
|
p.playersPoiList[locationID] = make([]int32, 0)
|
|
}
|
|
}
|
|
|
|
// SetSideSpeed sets the player's side movement speed
|
|
func (p *Player) SetSideSpeed(sideSpeed float32, updateFlags bool) {
|
|
// TODO: Implement when appearance system is available
|
|
charID := p.GetCharacterID()
|
|
if playerMovementData[charID] == nil {
|
|
playerMovementData[charID] = make(map[string]float32)
|
|
}
|
|
playerMovementData[charID]["side_speed"] = sideSpeed
|
|
}
|
|
|
|
// GetSideSpeed returns the player's side movement speed
|
|
func (p *Player) GetSideSpeed() float32 {
|
|
return p.GetPos("side_speed")
|
|
}
|
|
|
|
// SetVertSpeed sets the player's vertical movement speed
|
|
func (p *Player) SetVertSpeed(vertSpeed float32, updateFlags bool) {
|
|
// TODO: Implement when appearance system is available
|
|
charID := p.GetCharacterID()
|
|
if playerMovementData[charID] == nil {
|
|
playerMovementData[charID] = make(map[string]float32)
|
|
}
|
|
playerMovementData[charID]["vert_speed"] = vertSpeed
|
|
}
|
|
|
|
// GetVertSpeed returns the player's vertical movement speed
|
|
func (p *Player) GetVertSpeed() float32 {
|
|
return p.GetPos("vert_speed")
|
|
}
|
|
|
|
// SetClientHeading1 sets the client heading 1
|
|
func (p *Player) SetClientHeading1(heading float32, updateFlags bool) {
|
|
// TODO: Implement when appearance system is available
|
|
charID := p.GetCharacterID()
|
|
if playerMovementData[charID] == nil {
|
|
playerMovementData[charID] = make(map[string]float32)
|
|
}
|
|
playerMovementData[charID]["client_heading1"] = heading
|
|
}
|
|
|
|
// GetClientHeading1 returns the client heading 1
|
|
func (p *Player) GetClientHeading1() float32 {
|
|
return p.GetPos("client_heading1")
|
|
}
|
|
|
|
// SetClientHeading2 sets the client heading 2
|
|
func (p *Player) SetClientHeading2(heading float32, updateFlags bool) {
|
|
// TODO: Implement when appearance system is available
|
|
charID := p.GetCharacterID()
|
|
if playerMovementData[charID] == nil {
|
|
playerMovementData[charID] = make(map[string]float32)
|
|
}
|
|
playerMovementData[charID]["client_heading2"] = heading
|
|
}
|
|
|
|
// GetClientHeading2 returns the client heading 2
|
|
func (p *Player) GetClientHeading2() float32 {
|
|
return p.GetPos("client_heading2")
|
|
}
|
|
|
|
// SetClientPitch sets the client pitch
|
|
func (p *Player) SetClientPitch(pitch float32, updateFlags bool) {
|
|
// TODO: Implement when appearance system is available
|
|
charID := p.GetCharacterID()
|
|
if playerMovementData[charID] == nil {
|
|
playerMovementData[charID] = make(map[string]float32)
|
|
}
|
|
playerMovementData[charID]["client_pitch"] = pitch
|
|
}
|
|
|
|
// GetClientPitch returns the client pitch
|
|
func (p *Player) GetClientPitch() float32 {
|
|
return p.GetPos("client_pitch")
|
|
}
|
|
|
|
// IsResurrecting returns whether the player is currently resurrecting
|
|
func (p *Player) IsResurrecting() bool {
|
|
return p.resurrecting
|
|
}
|
|
|
|
// SetResurrecting sets the player's resurrection state
|
|
func (p *Player) SetResurrecting(val bool) {
|
|
p.resurrecting = val
|
|
}
|
|
|
|
// GetAwayMessage returns the player's away message
|
|
func (p *Player) GetAwayMessage() string {
|
|
return p.awayMessage
|
|
}
|
|
|
|
// SetAwayMessage sets the player's away message
|
|
func (p *Player) SetAwayMessage(message string) {
|
|
p.awayMessage = message
|
|
}
|
|
|
|
// GetIsTracking returns whether the player is tracking
|
|
func (p *Player) GetIsTracking() bool {
|
|
return p.isTracking
|
|
}
|
|
|
|
// SetIsTracking sets the player's tracking state
|
|
func (p *Player) SetIsTracking(val bool) {
|
|
p.isTracking = val
|
|
}
|
|
|
|
// GetBiography returns the player's biography
|
|
func (p *Player) GetBiography() string {
|
|
return p.biography
|
|
}
|
|
|
|
// SetBiography sets the player's biography
|
|
func (p *Player) SetBiography(bio string) {
|
|
p.biography = bio
|
|
}
|
|
|
|
// GetGuild returns the player's guild
|
|
func (p *Player) GetGuild() *Guild {
|
|
return p.guild
|
|
}
|
|
|
|
// SetGuild sets the player's guild
|
|
func (p *Player) SetGuild(guild *Guild) {
|
|
p.guild = guild
|
|
}
|
|
|
|
// SetPendingDeletion marks the player for deletion
|
|
func (p *Player) SetPendingDeletion(val bool) {
|
|
p.pendingDeletion = val
|
|
}
|
|
|
|
// GetPendingDeletion returns whether the player is marked for deletion
|
|
func (p *Player) GetPendingDeletion() bool {
|
|
return p.pendingDeletion
|
|
}
|
|
|
|
// GetPosPacketSpeed returns the position packet speed
|
|
func (p *Player) GetPosPacketSpeed() float32 {
|
|
return p.posPacketSpeed
|
|
}
|
|
|
|
// IsReturningFromLD returns whether the player is returning from linkdead
|
|
func (p *Player) IsReturningFromLD() bool {
|
|
return p.returningFromLD
|
|
}
|
|
|
|
// SetReturningFromLD sets whether the player is returning from linkdead
|
|
func (p *Player) SetReturningFromLD(val bool) {
|
|
p.returningFromLD = val
|
|
}
|
|
|
|
// GetLastMovementActivity returns the last movement activity value
|
|
func (p *Player) GetLastMovementActivity() int16 {
|
|
return p.lastMovementActivity
|
|
}
|
|
|
|
// SetRangeAttack sets whether the player is using ranged attacks
|
|
func (p *Player) SetRangeAttack(val bool) {
|
|
p.rangeAttack = val
|
|
}
|
|
|
|
// GetRangeAttack returns whether the player is using ranged attacks
|
|
func (p *Player) GetRangeAttack() bool {
|
|
return p.rangeAttack
|
|
}
|
|
|
|
// HasGMVision returns whether the player has GM vision enabled
|
|
func (p *Player) HasGMVision() bool {
|
|
return p.gmVision
|
|
}
|
|
|
|
// SetGMVision sets the player's GM vision state
|
|
func (p *Player) SetGMVision(val bool) {
|
|
p.gmVision = val
|
|
}
|
|
|
|
// GetCurrentLanguage returns the player's current language ID
|
|
func (p *Player) GetCurrentLanguage() int32 {
|
|
return p.currentLanguageID
|
|
}
|
|
|
|
// SetCurrentLanguage sets the player's current language ID
|
|
func (p *Player) SetCurrentLanguage(languageID int32) {
|
|
p.currentLanguageID = languageID
|
|
}
|
|
|
|
// SetActiveReward sets whether the player has an active reward
|
|
func (p *Player) SetActiveReward(val bool) {
|
|
p.activeReward = val
|
|
}
|
|
|
|
// IsActiveReward returns whether the player has an active reward
|
|
func (p *Player) IsActiveReward() bool {
|
|
return p.activeReward
|
|
}
|
|
|
|
// GetActiveFoodUniqueID returns the active food item's unique ID
|
|
func (p *Player) GetActiveFoodUniqueID() int64 {
|
|
return p.activeFoodUniqueID.Load()
|
|
}
|
|
|
|
// SetActiveFoodUniqueID sets the active food item's unique ID
|
|
func (p *Player) SetActiveFoodUniqueID(uniqueID int32, updateDB bool) {
|
|
p.activeFoodUniqueID.Store(int64(uniqueID))
|
|
if updateDB {
|
|
// TODO: Update database
|
|
}
|
|
}
|
|
|
|
// GetActiveDrinkUniqueID returns the active drink item's unique ID
|
|
func (p *Player) GetActiveDrinkUniqueID() int64 {
|
|
return p.activeDrinkUniqueID.Load()
|
|
}
|
|
|
|
// SetActiveDrinkUniqueID sets the active drink item's unique ID
|
|
func (p *Player) SetActiveDrinkUniqueID(uniqueID int32, updateDB bool) {
|
|
p.activeDrinkUniqueID.Store(int64(uniqueID))
|
|
if updateDB {
|
|
// TODO: Update database
|
|
}
|
|
}
|
|
|
|
// GetHouseVaultSlots returns the number of house vault slots
|
|
func (p *Player) GetHouseVaultSlots() int8 {
|
|
return p.houseVaultSlots
|
|
}
|
|
|
|
// SetHouseVaultSlots sets the number of house vault slots
|
|
func (p *Player) SetHouseVaultSlots(slots int8) {
|
|
p.houseVaultSlots = slots
|
|
}
|
|
|
|
// GetCurrentRecipe returns the current recipe ID
|
|
func (p *Player) GetCurrentRecipe() int32 {
|
|
return p.currentRecipe
|
|
}
|
|
|
|
// SetCurrentRecipe sets the current recipe ID
|
|
func (p *Player) SetCurrentRecipe(recipeID int32) {
|
|
p.currentRecipe = recipeID
|
|
}
|
|
|
|
// GetTempMount returns the temporary mount model ID
|
|
func (p *Player) GetTempMount() int32 {
|
|
return p.tmpMountModel
|
|
}
|
|
|
|
// SetTempMount sets the temporary mount model ID
|
|
func (p *Player) SetTempMount(id int32) {
|
|
p.tmpMountModel = id
|
|
}
|
|
|
|
// GetTempMountColor returns the temporary mount color
|
|
func (p *Player) GetTempMountColor() common.EQ2Color {
|
|
return p.tmpMountColor
|
|
}
|
|
|
|
// SetTempMountColor sets the temporary mount color
|
|
func (p *Player) SetTempMountColor(color *common.EQ2Color) {
|
|
p.tmpMountColor = *color
|
|
}
|
|
|
|
// GetTempMountSaddleColor returns the temporary mount saddle color
|
|
func (p *Player) GetTempMountSaddleColor() common.EQ2Color {
|
|
return p.tmpMountSaddleColor
|
|
}
|
|
|
|
// SetTempMountSaddleColor sets the temporary mount saddle color
|
|
func (p *Player) SetTempMountSaddleColor(color *common.EQ2Color) {
|
|
p.tmpMountSaddleColor = *color
|
|
}
|
|
|
|
// StopSaveSpellEffects returns whether to stop saving spell effects
|
|
func (p *Player) StopSaveSpellEffects() bool {
|
|
return p.stopSaveSpellEffects
|
|
}
|
|
|
|
// SetSaveSpellEffects sets whether to save spell effects
|
|
func (p *Player) SetSaveSpellEffects(val bool) {
|
|
p.stopSaveSpellEffects = !val
|
|
}
|
|
|
|
// ResetMentorship checks and resets mentorship if needed
|
|
func (p *Player) ResetMentorship() bool {
|
|
mentorshipStatus := p.resetMentorship
|
|
if mentorshipStatus {
|
|
p.SetMentorStats(int32(p.GetLevel()), 0, true)
|
|
}
|
|
p.resetMentorship = false
|
|
return mentorshipStatus
|
|
}
|
|
|
|
// EnableResetMentorship enables mentorship reset
|
|
func (p *Player) EnableResetMentorship() {
|
|
p.resetMentorship = true
|
|
}
|
|
|
|
// StopCombat stops all combat based on type
|
|
func (p *Player) StopCombat(combatType int8) {
|
|
switch combatType {
|
|
case 2:
|
|
p.SetRangeAttack(false)
|
|
p.InCombat(false, true)
|
|
default:
|
|
p.InCombat(false, false)
|
|
p.InCombat(false, true)
|
|
p.SetRangeAttack(false)
|
|
}
|
|
}
|
|
|
|
// GetPVPAlignment returns the player's PVP alignment
|
|
func (p *Player) GetPVPAlignment() int {
|
|
// TODO: Implement PVP alignment logic
|
|
return 0
|
|
}
|
|
|
|
// InitXPTable initializes the global XP requirements table
|
|
func InitXPTable() {
|
|
xpTableOnce.Do(func() {
|
|
levelXPReq = make(map[int8]int32)
|
|
// TODO: Load XP requirements from database or config
|
|
// For now, using placeholder values
|
|
for i := int8(1); i <= 100; i++ { // Reasonable level cap of 100
|
|
levelXPReq[i] = int32(i) * 1000
|
|
}
|
|
})
|
|
}
|
|
|
|
// GetNeededXPByLevel returns the XP needed for a specific level
|
|
func GetNeededXPByLevel(level int8) int32 {
|
|
InitXPTable()
|
|
if xp, exists := levelXPReq[level]; exists {
|
|
return xp
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// Cleanup performs cleanup when the player is being destroyed
|
|
func (p *Player) Cleanup() {
|
|
// Set save spell effects
|
|
p.SetSaveSpellEffects(true)
|
|
|
|
// Clear spells
|
|
for range p.spells {
|
|
// Individual elements will be cleared when slice is nilled
|
|
}
|
|
p.spells = nil
|
|
|
|
// Clear quickbar
|
|
for range p.quickbarItems {
|
|
// Individual elements will be cleared when slice is nilled
|
|
}
|
|
p.quickbarItems = nil
|
|
|
|
// Clear quest spawn requirements
|
|
p.playerSpawnQuestsRequiredMutex.Lock()
|
|
for range p.playerSpawnQuestsRequired {
|
|
// Individual elements will be cleared when map is nilled
|
|
}
|
|
p.playerSpawnQuestsRequired = nil
|
|
p.playerSpawnQuestsRequiredMutex.Unlock()
|
|
|
|
// Clear history spawn requirements
|
|
p.playerSpawnHistoryRequiredMutex.Lock()
|
|
for range p.playerSpawnHistoryRequired {
|
|
// Individual elements will be cleared when map is nilled
|
|
}
|
|
p.playerSpawnHistoryRequired = nil
|
|
p.playerSpawnHistoryRequiredMutex.Unlock()
|
|
|
|
// Clear character history
|
|
for range p.characterHistory {
|
|
// Individual elements will be cleared when map is nilled
|
|
}
|
|
p.characterHistory = nil
|
|
|
|
// Clear LUA history
|
|
p.luaHistoryMutex.Lock()
|
|
for range p.charLuaHistory {
|
|
// Individual elements will be cleared when map is nilled
|
|
}
|
|
p.charLuaHistory = nil
|
|
p.luaHistoryMutex.Unlock()
|
|
|
|
// Clear movement packets
|
|
p.movementPacket = nil
|
|
p.oldMovementPacket = nil
|
|
p.spawnTmpInfoXorPacket = nil
|
|
p.spawnTmpVisXorPacket = nil
|
|
p.spawnTmpPosXorPacket = nil
|
|
p.spellXorPacket = nil
|
|
p.spellOrigPacket = nil
|
|
p.raidOrigPacket = nil
|
|
p.raidXorPacket = nil
|
|
|
|
// Destroy quests
|
|
p.DestroyQuests()
|
|
|
|
// Write and remove statistics
|
|
p.WritePlayerStatistics()
|
|
p.RemovePlayerStatistics()
|
|
|
|
// Delete mail
|
|
p.DeleteMail(false)
|
|
|
|
// TODO: Remove from world lotto
|
|
// world.RemoveLottoPlayer(p.GetCharacterID())
|
|
|
|
// Clear player info
|
|
p.info = nil
|
|
|
|
// Clear spawn maps
|
|
p.indexMutex.Lock()
|
|
p.playerSpawnReverseIDMap = nil
|
|
p.playerSpawnIDMap = nil
|
|
p.indexMutex.Unlock()
|
|
|
|
// Clear packet lists
|
|
p.infoMutex.Lock()
|
|
p.spawnInfoPacketList = nil
|
|
p.infoMutex.Unlock()
|
|
|
|
p.visMutex.Lock()
|
|
p.spawnVisPacketList = nil
|
|
p.visMutex.Unlock()
|
|
|
|
p.posMutex.Lock()
|
|
p.spawnPosPacketList = nil
|
|
p.posMutex.Unlock()
|
|
|
|
// Clear packet structures
|
|
p.spawnHeaderStruct = nil
|
|
p.spawnFooterStruct = nil
|
|
p.signFooterStruct = nil
|
|
p.widgetFooterStruct = nil
|
|
p.spawnInfoStruct = nil
|
|
p.spawnVisStruct = nil
|
|
p.spawnPosStruct = nil
|
|
|
|
// Clear pending rewards
|
|
p.ClearPendingSelectableItemRewards(0, true)
|
|
p.ClearPendingItemRewards()
|
|
|
|
// Clear everything else
|
|
p.ClearEverything()
|
|
|
|
// Clear traits
|
|
p.sortedTraitList = nil
|
|
p.classTraining = nil
|
|
p.raceTraits = nil
|
|
p.innateRaceTraits = nil
|
|
p.focusEffects = nil
|
|
|
|
// Clear language list
|
|
p.playerLanguagesList.Clear()
|
|
}
|
|
|
|
// DestroyQuests cleans up all quest data
|
|
func (p *Player) DestroyQuests() {
|
|
p.playerQuestsMutex.Lock()
|
|
defer p.playerQuestsMutex.Unlock()
|
|
|
|
// Clear completed quests
|
|
for id, quest := range p.completedQuests {
|
|
delete(p.completedQuests, id)
|
|
_ = quest
|
|
}
|
|
|
|
// Clear active quests
|
|
for id, quest := range p.playerQuests {
|
|
delete(p.playerQuests, id)
|
|
_ = quest
|
|
}
|
|
|
|
// Clear pending quests
|
|
for id, quest := range p.pendingQuests {
|
|
delete(p.pendingQuests, id)
|
|
_ = quest
|
|
}
|
|
}
|
|
|
|
// WritePlayerStatistics saves player statistics to database
|
|
func (p *Player) WritePlayerStatistics() {
|
|
// TODO: Implement database write
|
|
}
|
|
|
|
// RemovePlayerStatistics removes all player statistics
|
|
func (p *Player) RemovePlayerStatistics() {
|
|
for id := range p.statistics {
|
|
delete(p.statistics, id)
|
|
}
|
|
}
|
|
|
|
// DeleteMail deletes all mail
|
|
func (p *Player) DeleteMail(fromDatabase bool) {
|
|
p.mailMutex.Lock()
|
|
defer p.mailMutex.Unlock()
|
|
|
|
for id, mail := range p.mailList {
|
|
if fromDatabase {
|
|
// TODO: Delete from database
|
|
}
|
|
delete(p.mailList, id)
|
|
_ = mail
|
|
}
|
|
}
|
|
|
|
// ClearPendingSelectableItemRewards clears pending selectable item rewards
|
|
func (p *Player) ClearPendingSelectableItemRewards(sourceID int32, all bool) {
|
|
if all {
|
|
for id, items := range p.pendingSelectableItemRewards {
|
|
for _, item := range items {
|
|
_ = item
|
|
}
|
|
delete(p.pendingSelectableItemRewards, id)
|
|
}
|
|
} else if sourceID > 0 {
|
|
if items, exists := p.pendingSelectableItemRewards[sourceID]; exists {
|
|
for _, item := range items {
|
|
_ = item
|
|
}
|
|
delete(p.pendingSelectableItemRewards, sourceID)
|
|
}
|
|
}
|
|
}
|
|
|
|
// ClearPendingItemRewards clears all pending item rewards
|
|
func (p *Player) ClearPendingItemRewards() {
|
|
for i := range p.pendingItemRewards {
|
|
p.pendingItemRewards[i] = Item{}
|
|
}
|
|
p.pendingItemRewards = nil
|
|
}
|
|
|
|
// ClearEverything performs final cleanup
|
|
func (p *Player) ClearEverything() {
|
|
// Clear friends list
|
|
for name := range p.friendList {
|
|
delete(p.friendList, name)
|
|
}
|
|
|
|
// Clear ignore list
|
|
for name := range p.ignoreList {
|
|
delete(p.ignoreList, name)
|
|
}
|
|
|
|
// Clear quests
|
|
p.playerQuestsMutex.Lock()
|
|
for id := range p.playerQuests {
|
|
delete(p.playerQuests, id)
|
|
}
|
|
for id := range p.completedQuests {
|
|
delete(p.completedQuests, id)
|
|
}
|
|
for id := range p.pendingQuests {
|
|
delete(p.pendingQuests, id)
|
|
}
|
|
p.playerQuestsMutex.Unlock()
|
|
|
|
// Clear other data
|
|
// TODO: Clear additional data as needed
|
|
}
|
|
|
|
// GetCharacterInstances returns the character instances manager
|
|
func (p *Player) GetCharacterInstances() *CharacterInstances {
|
|
return &p.characterInstances
|
|
}
|
|
|
|
// GetFactions returns the player's faction manager
|
|
func (p *Player) GetFactions() *PlayerFaction {
|
|
return &p.factions
|
|
}
|
|
|
|
// SetFactionValue sets a faction value for the player
|
|
func (p *Player) SetFactionValue(factionID int32, value int32) {
|
|
p.factions.SetFactionValue(factionID, value)
|
|
}
|
|
|
|
// GetCollectionList returns the player's collection list
|
|
func (p *Player) GetCollectionList() *PlayerCollectionList {
|
|
return &p.collectionList
|
|
}
|
|
|
|
// GetRecipeList returns the player's recipe list
|
|
func (p *Player) GetRecipeList() *PlayerRecipeList {
|
|
return &p.recipeList
|
|
}
|
|
|
|
// GetRecipeBookList returns the player's recipe book list
|
|
func (p *Player) GetRecipeBookList() *PlayerRecipeBookList {
|
|
return &p.recipebookList
|
|
}
|
|
|
|
// GetAchievementList returns the player's achievement list
|
|
func (p *Player) GetAchievementList() *PlayerAchievementList {
|
|
return &p.achievementList
|
|
}
|
|
|
|
// GetAchievementUpdateList returns the player's achievement update list
|
|
func (p *Player) GetAchievementUpdateList() *PlayerAchievementUpdateList {
|
|
return &p.achievementUpdateList
|
|
}
|
|
|
|
// GetPlayerTitles returns the player's titles list
|
|
func (p *Player) GetPlayerTitles() *PlayerTitlesList {
|
|
return &p.playerTitlesList
|
|
}
|
|
|
|
// GetPlayerLanguages returns the player's languages list
|
|
func (p *Player) GetPlayerLanguages() *PlayerLanguagesList {
|
|
return &p.playerLanguagesList
|
|
}
|
|
|
|
// GetPlayerItemList returns the player's item list
|
|
func (p *Player) GetPlayerItemList() *PlayerItemList {
|
|
return &p.itemList
|
|
}
|
|
|
|
// GetSkills returns the player's skill list
|
|
func (p *Player) GetSkills() *PlayerSkillList {
|
|
return &p.skillList
|
|
}
|
|
|
|
// AddGMVisualFilter adds a GM visual filter
|
|
func (p *Player) AddGMVisualFilter(filterType, filterValue int32, filterSearchStr string, visualTag int16) {
|
|
filter := GMTagFilter{
|
|
FilterType: filterType,
|
|
FilterValue: filterValue,
|
|
VisualTag: visualTag,
|
|
}
|
|
copy(filter.FilterSearchCriteria[:], filterSearchStr)
|
|
p.gmVisualFilters = append(p.gmVisualFilters, filter)
|
|
}
|
|
|
|
// ClearGMVisualFilters clears all GM visual filters
|
|
func (p *Player) ClearGMVisualFilters() {
|
|
p.gmVisualFilters = nil
|
|
}
|
|
|
|
// SetLevel sets the player's level in both spawn appearance and info struct
|
|
func (p *Player) SetLevel(level int16) {
|
|
// Update spawn appearance level
|
|
p.Spawn.SetLevel(level)
|
|
// Update info struct level
|
|
p.GetInfoStruct().SetLevel(level)
|
|
p.SetCharSheetChanged(true)
|
|
}
|
|
|
|
// macroIcons map - declared at package level since it was referenced but not in struct
|
|
var macroIcons map[int32]int16
|