703 lines
20 KiB
Go
703 lines
20 KiB
Go
package entity
|
|
|
|
import (
|
|
"math"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"eq2emu/internal/common"
|
|
"eq2emu/internal/spawn"
|
|
)
|
|
|
|
// Combat and pet types
|
|
const (
|
|
PetTypeSummon = 1
|
|
PetTypeCharm = 2
|
|
PetTypeDeity = 3
|
|
PetTypeCosmetic = 4
|
|
)
|
|
|
|
// Entity represents a combat-capable spawn (NPCs and Players)
|
|
// Extends the base Spawn with combat, magic, and equipment systems
|
|
type Entity struct {
|
|
*spawn.Spawn // Embedded spawn for basic functionality
|
|
|
|
// Core entity information
|
|
infoStruct *InfoStruct // All entity statistics and information
|
|
|
|
// Combat state
|
|
inCombat atomic.Bool // Whether currently in combat
|
|
casting atomic.Bool // Whether currently casting
|
|
maxSpeed float32 // Maximum movement speed
|
|
baseSpeed float32 // Base movement speed
|
|
speedMultiplier float32 // Speed multiplier
|
|
|
|
// Position tracking for movement
|
|
lastX float32 // Last known X position
|
|
lastY float32 // Last known Y position
|
|
lastZ float32 // Last known Z position
|
|
lastHeading float32 // Last known heading
|
|
|
|
// Regeneration
|
|
regenHpRate int16 // Health regeneration rate
|
|
regenPowerRate int16 // Power regeneration rate
|
|
|
|
// Spell and effect management
|
|
spellEffectManager *SpellEffectManager
|
|
|
|
// Pet system
|
|
pet *Entity // Summon pet
|
|
charmedPet *Entity // Charmed pet
|
|
deityPet *Entity // Deity pet
|
|
cosmeticPet *Entity // Cosmetic pet
|
|
owner int32 // Owner entity ID (if this is a pet)
|
|
petType int8 // Type of pet
|
|
petSpellID int32 // Spell ID used to create/control pet
|
|
petSpellTier int8 // Tier of pet spell
|
|
petDismissing atomic.Bool // Whether pet is being dismissed
|
|
|
|
// Group and social
|
|
// TODO: Add GroupMemberInfo when group system is implemented
|
|
// groupMemberInfo *GroupMemberInfo
|
|
|
|
// Trading
|
|
// TODO: Add Trade when trade system is implemented
|
|
// trade *Trade
|
|
|
|
// Deity and alignment
|
|
deity int8 // Deity ID
|
|
|
|
// Equipment and appearance
|
|
features common.CharFeatures // Character appearance features
|
|
equipment common.EQ2Equipment // Equipment appearance
|
|
|
|
// Threat and hate management
|
|
threatTransferPercent float32 // Percentage of threat to transfer
|
|
|
|
// Spell-related flags
|
|
hasSeeInvisSpell atomic.Bool // Has see invisible spell
|
|
hasSeeHideSpell atomic.Bool // Has see hidden spell
|
|
|
|
// Proc system
|
|
// TODO: Add proc list when implemented
|
|
// procList []Proc
|
|
|
|
// Thread safety
|
|
spellEffectMutex sync.RWMutex
|
|
maintainedMutex sync.RWMutex
|
|
detrimentalMutex sync.RWMutex
|
|
commandMutex sync.Mutex
|
|
bonusCalculationMutex sync.RWMutex
|
|
petMutex sync.RWMutex
|
|
}
|
|
|
|
// NewEntity creates a new Entity with default values
|
|
// Must be called by subclasses (NPC, Player) for proper initialization
|
|
func NewEntity() *Entity {
|
|
e := &Entity{
|
|
Spawn: spawn.NewSpawn(),
|
|
infoStruct: NewInfoStruct(),
|
|
maxSpeed: 6.0,
|
|
baseSpeed: 0.0,
|
|
speedMultiplier: 1.0,
|
|
lastX: -1,
|
|
lastY: -1,
|
|
lastZ: -1,
|
|
lastHeading: -1,
|
|
regenHpRate: 0,
|
|
regenPowerRate: 0,
|
|
spellEffectManager: NewSpellEffectManager(),
|
|
pet: nil,
|
|
charmedPet: nil,
|
|
deityPet: nil,
|
|
cosmeticPet: nil,
|
|
owner: 0,
|
|
petType: 0,
|
|
petSpellID: 0,
|
|
petSpellTier: 0,
|
|
deity: 0,
|
|
threatTransferPercent: 0.0,
|
|
}
|
|
|
|
// Initialize features and equipment
|
|
e.features = common.CharFeatures{}
|
|
e.equipment = common.EQ2Equipment{}
|
|
|
|
// Set initial state
|
|
e.inCombat.Store(false)
|
|
e.casting.Store(false)
|
|
e.petDismissing.Store(false)
|
|
e.hasSeeInvisSpell.Store(false)
|
|
e.hasSeeHideSpell.Store(false)
|
|
|
|
return e
|
|
}
|
|
|
|
// IsEntity returns true (implements Spawn interface)
|
|
func (e *Entity) IsEntity() bool {
|
|
return true
|
|
}
|
|
|
|
// GetInfoStruct returns the entity's info structure
|
|
func (e *Entity) GetInfoStruct() *InfoStruct {
|
|
return e.infoStruct
|
|
}
|
|
|
|
// SetInfoStruct updates the entity's info structure
|
|
func (e *Entity) SetInfoStruct(info *InfoStruct) {
|
|
if info != nil {
|
|
e.infoStruct = info
|
|
}
|
|
}
|
|
|
|
// GetClient returns the client for this entity (overridden by Player)
|
|
func (e *Entity) GetClient() interface{} {
|
|
return nil
|
|
}
|
|
|
|
// Combat state methods
|
|
|
|
// IsInCombat returns whether the entity is currently in combat
|
|
func (e *Entity) IsInCombat() bool {
|
|
return e.inCombat.Load()
|
|
}
|
|
|
|
// SetInCombat updates the combat state
|
|
func (e *Entity) SetInCombat(inCombat bool) {
|
|
e.inCombat.Store(inCombat)
|
|
}
|
|
|
|
// IsCasting returns whether the entity is currently casting
|
|
func (e *Entity) IsCasting() bool {
|
|
return e.casting.Load()
|
|
}
|
|
|
|
// SetCasting updates the casting state
|
|
func (e *Entity) SetCasting(casting bool) {
|
|
e.casting.Store(casting)
|
|
}
|
|
|
|
// Speed and movement methods
|
|
|
|
// GetMaxSpeed returns the maximum movement speed
|
|
func (e *Entity) GetMaxSpeed() float32 {
|
|
return e.maxSpeed
|
|
}
|
|
|
|
// SetMaxSpeed updates the maximum movement speed
|
|
func (e *Entity) SetMaxSpeed(speed float32) {
|
|
e.maxSpeed = speed
|
|
}
|
|
|
|
// GetBaseSpeed returns the base movement speed
|
|
func (e *Entity) GetBaseSpeed() float32 {
|
|
return e.baseSpeed
|
|
}
|
|
|
|
// SetBaseSpeed updates the base movement speed
|
|
func (e *Entity) SetBaseSpeed(speed float32) {
|
|
e.baseSpeed = speed
|
|
}
|
|
|
|
// GetSpeedMultiplier returns the speed multiplier
|
|
func (e *Entity) GetSpeedMultiplier() float32 {
|
|
return e.speedMultiplier
|
|
}
|
|
|
|
// SetSpeedMultiplier updates the speed multiplier
|
|
func (e *Entity) SetSpeedMultiplier(multiplier float32) {
|
|
e.speedMultiplier = multiplier
|
|
}
|
|
|
|
// CalculateEffectiveSpeed calculates the current effective speed
|
|
func (e *Entity) CalculateEffectiveSpeed() float32 {
|
|
baseSpeed := e.baseSpeed
|
|
if baseSpeed <= 0 {
|
|
baseSpeed = e.maxSpeed
|
|
}
|
|
|
|
return baseSpeed * e.speedMultiplier
|
|
}
|
|
|
|
// Position tracking methods
|
|
|
|
// GetLastPosition returns the last known position
|
|
func (e *Entity) GetLastPosition() (float32, float32, float32, float32) {
|
|
return e.lastX, e.lastY, e.lastZ, e.lastHeading
|
|
}
|
|
|
|
// SetLastPosition updates the last known position
|
|
func (e *Entity) SetLastPosition(x, y, z, heading float32) {
|
|
e.lastX = x
|
|
e.lastY = y
|
|
e.lastZ = z
|
|
e.lastHeading = heading
|
|
}
|
|
|
|
// HasMoved checks if the entity has moved since last position update
|
|
func (e *Entity) HasMoved() bool {
|
|
currentX := e.GetX()
|
|
currentY := e.GetY()
|
|
currentZ := e.GetZ()
|
|
currentHeading := e.GetHeading()
|
|
|
|
const epsilon = 0.01 // Small threshold for floating point comparison
|
|
|
|
return math.Abs(float64(currentX-e.lastX)) > epsilon ||
|
|
math.Abs(float64(currentY-e.lastY)) > epsilon ||
|
|
math.Abs(float64(currentZ-e.lastZ)) > epsilon ||
|
|
math.Abs(float64(currentHeading-e.lastHeading)) > epsilon
|
|
}
|
|
|
|
// Stat calculation methods
|
|
|
|
// GetStr returns the effective strength stat
|
|
func (e *Entity) GetStr() int16 {
|
|
base := int16(e.infoStruct.GetStr())
|
|
bonus := int16(e.spellEffectManager.GetBonusValue(1, 0, int16(e.infoStruct.GetRace()), 0)) // Stat type 1 = STR
|
|
return base + bonus
|
|
}
|
|
|
|
// GetSta returns the effective stamina stat
|
|
func (e *Entity) GetSta() int16 {
|
|
base := int16(e.infoStruct.GetSta())
|
|
bonus := int16(e.spellEffectManager.GetBonusValue(2, 0, int16(e.infoStruct.GetRace()), 0)) // Stat type 2 = STA
|
|
return base + bonus
|
|
}
|
|
|
|
// GetAgi returns the effective agility stat
|
|
func (e *Entity) GetAgi() int16 {
|
|
base := int16(e.infoStruct.GetAgi())
|
|
bonus := int16(e.spellEffectManager.GetBonusValue(3, 0, int16(e.infoStruct.GetRace()), 0)) // Stat type 3 = AGI
|
|
return base + bonus
|
|
}
|
|
|
|
// GetWis returns the effective wisdom stat
|
|
func (e *Entity) GetWis() int16 {
|
|
base := int16(e.infoStruct.GetWis())
|
|
bonus := int16(e.spellEffectManager.GetBonusValue(4, 0, int16(e.infoStruct.GetRace()), 0)) // Stat type 4 = WIS
|
|
return base + bonus
|
|
}
|
|
|
|
// GetIntel returns the effective intelligence stat
|
|
func (e *Entity) GetIntel() int16 {
|
|
base := int16(e.infoStruct.GetIntel())
|
|
bonus := int16(e.spellEffectManager.GetBonusValue(5, 0, int16(e.infoStruct.GetRace()), 0)) // Stat type 5 = INT
|
|
return base + bonus
|
|
}
|
|
|
|
// GetPrimaryStat returns the highest primary stat value
|
|
func (e *Entity) GetPrimaryStat() int16 {
|
|
str := e.GetStr()
|
|
sta := e.GetSta()
|
|
agi := e.GetAgi()
|
|
wis := e.GetWis()
|
|
intel := e.GetIntel()
|
|
|
|
primary := str
|
|
if sta > primary {
|
|
primary = sta
|
|
}
|
|
if agi > primary {
|
|
primary = agi
|
|
}
|
|
if wis > primary {
|
|
primary = wis
|
|
}
|
|
if intel > primary {
|
|
primary = intel
|
|
}
|
|
|
|
return primary
|
|
}
|
|
|
|
// Resistance methods
|
|
|
|
// GetHeatResistance returns heat resistance
|
|
func (e *Entity) GetHeatResistance() int16 {
|
|
return e.infoStruct.GetResistance("heat")
|
|
}
|
|
|
|
// GetColdResistance returns cold resistance
|
|
func (e *Entity) GetColdResistance() int16 {
|
|
return e.infoStruct.GetResistance("cold")
|
|
}
|
|
|
|
// GetMagicResistance returns magic resistance
|
|
func (e *Entity) GetMagicResistance() int16 {
|
|
return e.infoStruct.GetResistance("magic")
|
|
}
|
|
|
|
// GetMentalResistance returns mental resistance
|
|
func (e *Entity) GetMentalResistance() int16 {
|
|
return e.infoStruct.GetResistance("mental")
|
|
}
|
|
|
|
// GetDivineResistance returns divine resistance
|
|
func (e *Entity) GetDivineResistance() int16 {
|
|
return e.infoStruct.GetResistance("divine")
|
|
}
|
|
|
|
// GetDiseaseResistance returns disease resistance
|
|
func (e *Entity) GetDiseaseResistance() int16 {
|
|
return e.infoStruct.GetResistance("disease")
|
|
}
|
|
|
|
// GetPoisonResistance returns poison resistance
|
|
func (e *Entity) GetPoisonResistance() int16 {
|
|
return e.infoStruct.GetResistance("poison")
|
|
}
|
|
|
|
// Spell effect management methods
|
|
|
|
// AddMaintainedSpell adds a maintained spell effect
|
|
func (e *Entity) AddMaintainedSpell(name string, spellID int32, duration float32, concentration int8) bool {
|
|
// Check if we have enough concentration
|
|
if !e.infoStruct.AddConcentration(int16(concentration)) {
|
|
return false
|
|
}
|
|
|
|
effect := NewMaintainedEffects(name, spellID, duration)
|
|
effect.ConcUsed = concentration
|
|
|
|
e.maintainedMutex.Lock()
|
|
defer e.maintainedMutex.Unlock()
|
|
|
|
if !e.spellEffectManager.AddMaintainedEffect(effect) {
|
|
// Failed to add, return concentration
|
|
e.infoStruct.RemoveConcentration(int16(concentration))
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// RemoveMaintainedSpell removes a maintained spell effect
|
|
func (e *Entity) RemoveMaintainedSpell(spellID int32) bool {
|
|
e.maintainedMutex.Lock()
|
|
defer e.maintainedMutex.Unlock()
|
|
|
|
// Get the effect to check concentration usage
|
|
effect := e.spellEffectManager.GetMaintainedEffect(spellID)
|
|
if effect == nil {
|
|
return false
|
|
}
|
|
|
|
// Return concentration
|
|
e.infoStruct.RemoveConcentration(int16(effect.ConcUsed))
|
|
|
|
return e.spellEffectManager.RemoveMaintainedEffect(spellID)
|
|
}
|
|
|
|
// GetMaintainedSpell retrieves a maintained spell effect
|
|
func (e *Entity) GetMaintainedSpell(spellID int32) *MaintainedEffects {
|
|
e.maintainedMutex.RLock()
|
|
defer e.maintainedMutex.RUnlock()
|
|
|
|
return e.spellEffectManager.GetMaintainedEffect(spellID)
|
|
}
|
|
|
|
// AddSpellEffect adds a temporary spell effect
|
|
func (e *Entity) AddSpellEffect(spellID int32, casterID int32, duration float32) bool {
|
|
effect := NewSpellEffects(spellID, casterID, duration)
|
|
|
|
e.spellEffectMutex.Lock()
|
|
defer e.spellEffectMutex.Unlock()
|
|
|
|
return e.spellEffectManager.AddSpellEffect(effect)
|
|
}
|
|
|
|
// RemoveSpellEffect removes a spell effect
|
|
func (e *Entity) RemoveSpellEffect(spellID int32) bool {
|
|
e.spellEffectMutex.Lock()
|
|
defer e.spellEffectMutex.Unlock()
|
|
|
|
return e.spellEffectManager.RemoveSpellEffect(spellID)
|
|
}
|
|
|
|
// AddDetrimentalSpell adds a detrimental effect
|
|
func (e *Entity) AddDetrimentalSpell(spellID int32, casterID int32, duration float32, detType int8) {
|
|
effect := NewDetrimentalEffects(spellID, casterID, duration)
|
|
effect.DetType = detType
|
|
|
|
e.detrimentalMutex.Lock()
|
|
defer e.detrimentalMutex.Unlock()
|
|
|
|
e.spellEffectManager.AddDetrimentalEffect(*effect)
|
|
}
|
|
|
|
// RemoveDetrimentalSpell removes a detrimental effect
|
|
func (e *Entity) RemoveDetrimentalSpell(spellID int32, casterID int32) bool {
|
|
e.detrimentalMutex.Lock()
|
|
defer e.detrimentalMutex.Unlock()
|
|
|
|
return e.spellEffectManager.RemoveDetrimentalEffect(spellID, casterID)
|
|
}
|
|
|
|
// GetDetrimentalEffect retrieves a detrimental effect
|
|
func (e *Entity) GetDetrimentalEffect(spellID int32, casterID int32) *DetrimentalEffects {
|
|
e.detrimentalMutex.RLock()
|
|
defer e.detrimentalMutex.RUnlock()
|
|
|
|
return e.spellEffectManager.GetDetrimentalEffect(spellID, casterID)
|
|
}
|
|
|
|
// HasControlEffect checks if the entity has a specific control effect
|
|
func (e *Entity) HasControlEffect(controlType int8) bool {
|
|
return e.spellEffectManager.HasControlEffect(controlType)
|
|
}
|
|
|
|
// Bonus system methods
|
|
|
|
// AddSkillBonus adds a skill-related bonus
|
|
func (e *Entity) AddSkillBonus(spellID int32, skillID int32, value float32) {
|
|
bonus := NewBonusValues(spellID, int16(skillID+100), value) // Skill bonuses use type 100+
|
|
e.spellEffectManager.AddBonus(bonus)
|
|
}
|
|
|
|
// AddStatBonus adds a stat bonus
|
|
func (e *Entity) AddStatBonus(spellID int32, statType int16, value float32) {
|
|
bonus := NewBonusValues(spellID, statType, value)
|
|
e.spellEffectManager.AddBonus(bonus)
|
|
}
|
|
|
|
// CalculateBonuses recalculates all stat bonuses and updates InfoStruct
|
|
func (e *Entity) CalculateBonuses() {
|
|
e.bonusCalculationMutex.Lock()
|
|
defer e.bonusCalculationMutex.Unlock()
|
|
|
|
// Reset effects to base values
|
|
e.infoStruct.ResetEffects()
|
|
|
|
entityClass := int64(1 << e.infoStruct.GetClass1()) // Convert class to bitmask
|
|
race := int16(e.infoStruct.GetRace())
|
|
factionID := int32(e.GetFactionID())
|
|
|
|
// Apply stat bonuses
|
|
strBonus := e.spellEffectManager.GetBonusValue(1, entityClass, race, factionID)
|
|
staBonus := e.spellEffectManager.GetBonusValue(2, entityClass, race, factionID)
|
|
agiBonus := e.spellEffectManager.GetBonusValue(3, entityClass, race, factionID)
|
|
wisBonus := e.spellEffectManager.GetBonusValue(4, entityClass, race, factionID)
|
|
intelBonus := e.spellEffectManager.GetBonusValue(5, entityClass, race, factionID)
|
|
|
|
// Update InfoStruct with bonuses
|
|
e.infoStruct.SetStr(e.infoStruct.GetStr() + strBonus)
|
|
e.infoStruct.SetSta(e.infoStruct.GetSta() + staBonus)
|
|
e.infoStruct.SetAgi(e.infoStruct.GetAgi() + agiBonus)
|
|
e.infoStruct.SetWis(e.infoStruct.GetWis() + wisBonus)
|
|
e.infoStruct.SetIntel(e.infoStruct.GetIntel() + intelBonus)
|
|
|
|
// Apply resistance bonuses
|
|
heatBonus := int16(e.spellEffectManager.GetBonusValue(10, entityClass, race, factionID))
|
|
coldBonus := int16(e.spellEffectManager.GetBonusValue(11, entityClass, race, factionID))
|
|
magicBonus := int16(e.spellEffectManager.GetBonusValue(12, entityClass, race, factionID))
|
|
mentalBonus := int16(e.spellEffectManager.GetBonusValue(13, entityClass, race, factionID))
|
|
divineBonus := int16(e.spellEffectManager.GetBonusValue(14, entityClass, race, factionID))
|
|
diseaseBonus := int16(e.spellEffectManager.GetBonusValue(15, entityClass, race, factionID))
|
|
poisonBonus := int16(e.spellEffectManager.GetBonusValue(16, entityClass, race, factionID))
|
|
|
|
e.infoStruct.SetResistance("heat", e.infoStruct.GetResistance("heat")+heatBonus)
|
|
e.infoStruct.SetResistance("cold", e.infoStruct.GetResistance("cold")+coldBonus)
|
|
e.infoStruct.SetResistance("magic", e.infoStruct.GetResistance("magic")+magicBonus)
|
|
e.infoStruct.SetResistance("mental", e.infoStruct.GetResistance("mental")+mentalBonus)
|
|
e.infoStruct.SetResistance("divine", e.infoStruct.GetResistance("divine")+divineBonus)
|
|
e.infoStruct.SetResistance("disease", e.infoStruct.GetResistance("disease")+diseaseBonus)
|
|
e.infoStruct.SetResistance("poison", e.infoStruct.GetResistance("poison")+poisonBonus)
|
|
}
|
|
|
|
// Pet management methods
|
|
|
|
// GetPet returns the summon pet
|
|
func (e *Entity) GetPet() *Entity {
|
|
e.petMutex.RLock()
|
|
defer e.petMutex.RUnlock()
|
|
return e.pet
|
|
}
|
|
|
|
// SetPet sets the summon pet
|
|
func (e *Entity) SetPet(pet *Entity) {
|
|
e.petMutex.Lock()
|
|
defer e.petMutex.Unlock()
|
|
e.pet = pet
|
|
if pet != nil {
|
|
pet.owner = e.GetID()
|
|
pet.petType = PetTypeSummon
|
|
}
|
|
}
|
|
|
|
// GetCharmedPet returns the charmed pet
|
|
func (e *Entity) GetCharmedPet() *Entity {
|
|
e.petMutex.RLock()
|
|
defer e.petMutex.RUnlock()
|
|
return e.charmedPet
|
|
}
|
|
|
|
// SetCharmedPet sets the charmed pet
|
|
func (e *Entity) SetCharmedPet(pet *Entity) {
|
|
e.petMutex.Lock()
|
|
defer e.petMutex.Unlock()
|
|
e.charmedPet = pet
|
|
if pet != nil {
|
|
pet.owner = e.GetID()
|
|
pet.petType = PetTypeCharm
|
|
}
|
|
}
|
|
|
|
// GetDeityPet returns the deity pet
|
|
func (e *Entity) GetDeityPet() *Entity {
|
|
e.petMutex.RLock()
|
|
defer e.petMutex.RUnlock()
|
|
return e.deityPet
|
|
}
|
|
|
|
// SetDeityPet sets the deity pet
|
|
func (e *Entity) SetDeityPet(pet *Entity) {
|
|
e.petMutex.Lock()
|
|
defer e.petMutex.Unlock()
|
|
e.deityPet = pet
|
|
if pet != nil {
|
|
pet.owner = e.GetID()
|
|
pet.petType = PetTypeDeity
|
|
}
|
|
}
|
|
|
|
// GetCosmeticPet returns the cosmetic pet
|
|
func (e *Entity) GetCosmeticPet() *Entity {
|
|
e.petMutex.RLock()
|
|
defer e.petMutex.RUnlock()
|
|
return e.cosmeticPet
|
|
}
|
|
|
|
// SetCosmeticPet sets the cosmetic pet
|
|
func (e *Entity) SetCosmeticPet(pet *Entity) {
|
|
e.petMutex.Lock()
|
|
defer e.petMutex.Unlock()
|
|
e.cosmeticPet = pet
|
|
if pet != nil {
|
|
pet.owner = e.GetID()
|
|
pet.petType = PetTypeCosmetic
|
|
}
|
|
}
|
|
|
|
// GetOwner returns the owner entity ID (if this is a pet)
|
|
func (e *Entity) GetOwner() int32 {
|
|
return e.owner
|
|
}
|
|
|
|
// GetPetType returns the type of pet this entity is
|
|
func (e *Entity) GetPetType() int8 {
|
|
return e.petType
|
|
}
|
|
|
|
// IsPetDismissing returns whether the pet is being dismissed
|
|
func (e *Entity) IsPetDismissing() bool {
|
|
return e.petDismissing.Load()
|
|
}
|
|
|
|
// SetPetDismissing sets whether the pet is being dismissed
|
|
func (e *Entity) SetPetDismissing(dismissing bool) {
|
|
e.petDismissing.Store(dismissing)
|
|
}
|
|
|
|
// Utility methods
|
|
|
|
// GetDeity returns the deity ID
|
|
func (e *Entity) GetDeity() int8 {
|
|
return e.deity
|
|
}
|
|
|
|
// SetDeity updates the deity ID
|
|
func (e *Entity) SetDeity(deity int8) {
|
|
e.deity = deity
|
|
}
|
|
|
|
// GetDodgeChance calculates the dodge chance (to be overridden by subclasses)
|
|
func (e *Entity) GetDodgeChance() float32 {
|
|
// Base implementation, should be overridden
|
|
return 5.0 // 5% base dodge chance
|
|
}
|
|
|
|
// HasSeeInvisSpell returns whether the entity has see invisible
|
|
func (e *Entity) HasSeeInvisSpell() bool {
|
|
return e.hasSeeInvisSpell.Load()
|
|
}
|
|
|
|
// SetSeeInvisSpell updates the see invisible state
|
|
func (e *Entity) SetSeeInvisSpell(hasSpell bool) {
|
|
e.hasSeeInvisSpell.Store(hasSpell)
|
|
}
|
|
|
|
// HasSeeHideSpell returns whether the entity has see hidden
|
|
func (e *Entity) HasSeeHideSpell() bool {
|
|
return e.hasSeeHideSpell.Load()
|
|
}
|
|
|
|
// SetSeeHideSpell updates the see hidden state
|
|
func (e *Entity) SetSeeHideSpell(hasSpell bool) {
|
|
e.hasSeeHideSpell.Store(hasSpell)
|
|
}
|
|
|
|
// Cleanup methods
|
|
|
|
// DeleteSpellEffects removes all spell effects
|
|
func (e *Entity) DeleteSpellEffects(removeClient bool) {
|
|
e.spellEffectMutex.Lock()
|
|
e.maintainedMutex.Lock()
|
|
e.detrimentalMutex.Lock()
|
|
defer e.spellEffectMutex.Unlock()
|
|
defer e.maintainedMutex.Unlock()
|
|
defer e.detrimentalMutex.Unlock()
|
|
|
|
// Clear all maintained effects and return concentration
|
|
effects := e.spellEffectManager.GetAllMaintainedEffects()
|
|
for _, effect := range effects {
|
|
e.infoStruct.RemoveConcentration(int16(effect.ConcUsed))
|
|
}
|
|
|
|
e.spellEffectManager.ClearAllEffects()
|
|
}
|
|
|
|
// RemoveSpells removes spell effects, optionally only unfriendly ones
|
|
func (e *Entity) RemoveSpells(unfriendlyOnly bool) {
|
|
// TODO: Implement when we can determine friendly vs unfriendly spells
|
|
if !unfriendlyOnly {
|
|
e.DeleteSpellEffects(false)
|
|
}
|
|
}
|
|
|
|
// Update methods
|
|
|
|
// ProcessEffects handles periodic effect processing
|
|
func (e *Entity) ProcessEffects() {
|
|
// Clean up expired effects
|
|
e.spellEffectManager.CleanupExpiredEffects()
|
|
|
|
// TODO: Apply periodic effect damage/healing
|
|
// TODO: Handle effect-based stat changes
|
|
}
|
|
|
|
// Class system integration adapters
|
|
|
|
// GetClass returns the entity's primary class (ClassAware interface compatibility)
|
|
func (e *Entity) GetClass() int8 {
|
|
return e.infoStruct.GetClass1()
|
|
}
|
|
|
|
// SetClass sets the entity's primary class (ClassAware interface compatibility)
|
|
func (e *Entity) SetClass(classID int8) {
|
|
e.infoStruct.SetClass1(classID)
|
|
}
|
|
|
|
// GetLevel returns the entity's level (EntityWithClass interface compatibility)
|
|
func (e *Entity) GetLevel() int8 {
|
|
return int8(e.infoStruct.GetLevel())
|
|
}
|
|
|
|
// TODO: Additional methods to implement:
|
|
// - Combat calculation methods (damage, healing, etc.)
|
|
// - Equipment bonus application methods
|
|
// - Spell casting methods
|
|
// - Threat and hate management methods
|
|
// - Group combat methods
|
|
// - Many more combat and magic related methods
|