eq2go/internal/player/spawn_management.go

387 lines
9.4 KiB
Go

package player
import (
"time"
"eq2emu/internal/entity"
)
// WasSentSpawn checks if a spawn was already sent to the player
func (p *Player) WasSentSpawn(spawnID int32) bool {
p.spawnMutex.Lock()
defer p.spawnMutex.Unlock()
if state, exists := p.spawnPacketSent[spawnID]; exists {
return state == int8(SPAWN_STATE_SENT)
}
return false
}
// IsSendingSpawn checks if a spawn is currently being sent
func (p *Player) IsSendingSpawn(spawnID int32) bool {
p.spawnMutex.Lock()
defer p.spawnMutex.Unlock()
if state, exists := p.spawnPacketSent[spawnID]; exists {
return state == int8(SPAWN_STATE_SENDING)
}
return false
}
// IsRemovingSpawn checks if a spawn is being removed
func (p *Player) IsRemovingSpawn(spawnID int32) bool {
p.spawnMutex.Lock()
defer p.spawnMutex.Unlock()
if state, exists := p.spawnPacketSent[spawnID]; exists {
return state == int8(SPAWN_STATE_REMOVING)
}
return false
}
// SetSpawnSentState sets the spawn state for tracking
func (p *Player) SetSpawnSentState(spawn *entity.Spawn, state SpawnState) bool {
if spawn == nil {
return false
}
p.spawnMutex.Lock()
defer p.spawnMutex.Unlock()
spawnID := spawn.GetDatabaseID()
p.spawnPacketSent[spawnID] = int8(state)
// Handle state-specific logic
switch state {
case SPAWN_STATE_SENT_WAIT:
if queueState, exists := p.spawnStateList[spawnID]; exists {
queueState.SpawnStateTimer = time.Now().Add(500 * time.Millisecond)
} else {
p.spawnStateList[spawnID] = &SpawnQueueState{
SpawnStateTimer: time.Now().Add(500 * time.Millisecond),
IndexID: p.GetIndexForSpawn(spawn),
}
}
case SPAWN_STATE_REMOVING_SLEEP:
if queueState, exists := p.spawnStateList[spawnID]; exists {
queueState.SpawnStateTimer = time.Now().Add(10 * time.Second)
} else {
p.spawnStateList[spawnID] = &SpawnQueueState{
SpawnStateTimer: time.Now().Add(10 * time.Second),
IndexID: p.GetIndexForSpawn(spawn),
}
}
}
return true
}
// CheckSpawnStateQueue checks spawn states and updates as needed
func (p *Player) CheckSpawnStateQueue() {
p.spawnMutex.Lock()
defer p.spawnMutex.Unlock()
now := time.Now()
for spawnID, queueState := range p.spawnStateList {
if now.After(queueState.SpawnStateTimer) {
if state, exists := p.spawnPacketSent[spawnID]; exists {
switch SpawnState(state) {
case SPAWN_STATE_SENT_WAIT:
p.spawnPacketSent[spawnID] = int8(SPAWN_STATE_SENT)
delete(p.spawnStateList, spawnID)
case SPAWN_STATE_REMOVING_SLEEP:
// TODO: Remove spawn from index
p.spawnPacketSent[spawnID] = int8(SPAWN_STATE_REMOVED)
delete(p.spawnStateList, spawnID)
}
}
}
}
}
// GetSpawnWithPlayerID returns a spawn by player-specific ID
func (p *Player) GetSpawnWithPlayerID(id int32) *entity.Spawn {
p.indexMutex.RLock()
defer p.indexMutex.RUnlock()
if spawn, exists := p.playerSpawnIDMap[id]; exists {
return spawn
}
return nil
}
// GetIDWithPlayerSpawn returns the player-specific ID for a spawn
func (p *Player) GetIDWithPlayerSpawn(spawn *entity.Spawn) int32 {
if spawn == nil {
return 0
}
p.indexMutex.RLock()
defer p.indexMutex.RUnlock()
if id, exists := p.playerSpawnReverseIDMap[spawn]; exists {
return id
}
return 0
}
// GetNextSpawnIndex returns the next available spawn index
func (p *Player) GetNextSpawnIndex(spawn *entity.Spawn, setLock bool) int16 {
if setLock {
p.indexMutex.Lock()
defer p.indexMutex.Unlock()
}
// Start from current index and find next available
for i := p.spawnIndex + 1; i != p.spawnIndex; i++ {
if i > 9999 { // Wrap around
i = 1
}
if _, exists := p.playerSpawnIDMap[int32(i)]; !exists {
p.spawnIndex = i
return i
}
}
// If we've looped all the way around, increment and use it anyway
p.spawnIndex++
if p.spawnIndex > 9999 {
p.spawnIndex = 1
}
return p.spawnIndex
}
// SetSpawnMap adds a spawn to the player's spawn map
func (p *Player) SetSpawnMap(spawn *entity.Spawn) bool {
if spawn == nil {
return false
}
p.indexMutex.Lock()
defer p.indexMutex.Unlock()
// Check if spawn already has an ID
if id, exists := p.playerSpawnReverseIDMap[spawn]; exists && id > 0 {
return true
}
// Get next available index
index := p.GetNextSpawnIndex(spawn, false)
// Set bidirectional mapping
p.playerSpawnIDMap[int32(index)] = spawn
p.playerSpawnReverseIDMap[spawn] = int32(index)
return true
}
// SetSpawnMapIndex sets a specific index for a spawn
func (p *Player) SetSpawnMapIndex(spawn *entity.Spawn, index int32) {
p.indexMutex.Lock()
defer p.indexMutex.Unlock()
p.playerSpawnIDMap[index] = spawn
p.playerSpawnReverseIDMap[spawn] = index
}
// SetSpawnMapAndIndex sets spawn in map and returns the index
func (p *Player) SetSpawnMapAndIndex(spawn *entity.Spawn) int16 {
if spawn == nil {
return 0
}
p.indexMutex.Lock()
defer p.indexMutex.Unlock()
// Check if spawn already has an ID
if id, exists := p.playerSpawnReverseIDMap[spawn]; exists && id > 0 {
return int16(id)
}
// Get next available index
index := p.GetNextSpawnIndex(spawn, false)
// Set bidirectional mapping
p.playerSpawnIDMap[int32(index)] = spawn
p.playerSpawnReverseIDMap[spawn] = int32(index)
return index
}
// GetSpawnByIndex returns a spawn by its player-specific index
func (p *Player) GetSpawnByIndex(index int16) *entity.Spawn {
return p.GetSpawnWithPlayerID(int32(index))
}
// GetIndexForSpawn returns the player-specific index for a spawn
func (p *Player) GetIndexForSpawn(spawn *entity.Spawn) int16 {
return int16(p.GetIDWithPlayerSpawn(spawn))
}
// WasSpawnRemoved checks if a spawn was removed
func (p *Player) WasSpawnRemoved(spawn *entity.Spawn) bool {
if spawn == nil {
return false
}
p.spawnMutex.Lock()
defer p.spawnMutex.Unlock()
spawnID := spawn.GetDatabaseID()
if state, exists := p.spawnPacketSent[spawnID]; exists {
return state == int8(SPAWN_STATE_REMOVED)
}
return false
}
// ResetSpawnPackets resets spawn packet state for a spawn
func (p *Player) ResetSpawnPackets(id int32) {
p.spawnMutex.Lock()
defer p.spawnMutex.Unlock()
delete(p.spawnPacketSent, id)
delete(p.spawnStateList, id)
}
// RemoveSpawn removes a spawn from the player's view
func (p *Player) RemoveSpawn(spawn *entity.Spawn, deleteSpawn bool) {
if spawn == nil {
return
}
// Get the player index for this spawn
index := p.GetIDWithPlayerSpawn(spawn)
if index == 0 {
return
}
// Remove from spawn maps
p.indexMutex.Lock()
delete(p.playerSpawnIDMap, index)
delete(p.playerSpawnReverseIDMap, spawn)
p.indexMutex.Unlock()
// Remove spawn packets
spawnID := spawn.GetDatabaseID()
p.infoMutex.Lock()
delete(p.spawnInfoPacketList, spawnID)
p.infoMutex.Unlock()
p.visMutex.Lock()
delete(p.spawnVisPacketList, spawnID)
p.visMutex.Unlock()
p.posMutex.Lock()
delete(p.spawnPosPacketList, spawnID)
p.posMutex.Unlock()
// Reset spawn state
p.ResetSpawnPackets(spawnID)
// TODO: Send despawn packet to client
if deleteSpawn {
// TODO: Actually delete the spawn if requested
}
}
// ShouldSendSpawn determines if a spawn should be sent to player
func (p *Player) ShouldSendSpawn(spawn *entity.Spawn) bool {
if spawn == nil {
return false
}
// Don't send self
if spawn == &p.Entity.Spawn {
return false
}
// Check if already sent
if p.WasSentSpawn(spawn.GetDatabaseID()) {
return false
}
// Check distance
distance := p.GetDistance(spawn)
maxDistance := float32(200.0) // TODO: Get from rule system
if distance > maxDistance {
return false
}
// TODO: Check visibility flags, stealth, etc.
return true
}
// SetSpawnDeleteTime sets the time when a spawn should be deleted
func (p *Player) SetSpawnDeleteTime(id int32, deleteTime int32) {
// TODO: Implement spawn deletion timer
}
// GetSpawnDeleteTime gets the deletion time for a spawn
func (p *Player) GetSpawnDeleteTime(id int32) int32 {
// TODO: Implement spawn deletion timer
return 0
}
// ClearRemovalTimers clears all spawn removal timers
func (p *Player) ClearRemovalTimers() {
// TODO: Implement spawn deletion timer clearing
}
// ResetSavedSpawns resets all saved spawn data
func (p *Player) ResetSavedSpawns() {
p.indexMutex.Lock()
p.playerSpawnIDMap = make(map[int32]*entity.Spawn)
p.playerSpawnReverseIDMap = make(map[*entity.Spawn]int32)
// Re-add self
p.playerSpawnIDMap[1] = &p.Entity.Spawn
p.playerSpawnReverseIDMap[&p.Entity.Spawn] = 1
p.indexMutex.Unlock()
p.spawnMutex.Lock()
p.spawnPacketSent = make(map[int32]int8)
p.spawnStateList = make(map[int32]*SpawnQueueState)
p.spawnMutex.Unlock()
p.infoMutex.Lock()
p.spawnInfoPacketList = make(map[int32]string)
p.infoMutex.Unlock()
p.visMutex.Lock()
p.spawnVisPacketList = make(map[int32]string)
p.visMutex.Unlock()
p.posMutex.Lock()
p.spawnPosPacketList = make(map[int32]string)
p.posMutex.Unlock()
}
// IsSpawnInRangeList checks if a spawn is in the range list
func (p *Player) IsSpawnInRangeList(spawnID int32) bool {
p.spawnAggroRangeMutex.RLock()
defer p.spawnAggroRangeMutex.RUnlock()
_, exists := p.playerAggroRangeSpawns[spawnID]
return exists
}
// SetSpawnInRangeList sets whether a spawn is in range
func (p *Player) SetSpawnInRangeList(spawnID int32, inRange bool) {
p.spawnAggroRangeMutex.Lock()
defer p.spawnAggroRangeMutex.Unlock()
if inRange {
p.playerAggroRangeSpawns[spawnID] = true
} else {
delete(p.playerAggroRangeSpawns, spawnID)
}
}
// ProcessSpawnRangeUpdates processes spawn range updates
func (p *Player) ProcessSpawnRangeUpdates() {
// TODO: Implement spawn range update processing
// This would check all spawns in range and update visibility
}