package skills import ( "math/rand" "sync" ) // PlayerSkillList manages skills for a specific player type PlayerSkillList struct { skills map[int32]*Skill // Player's skills by ID nameSkillMap map[string]*Skill // Skills by name for quick lookup skillUpdates []*Skill // Skills needing updates skillBonusList map[int32]*SkillBonus // Skill bonuses by spell ID // Packet data for skill updates origPacket []byte xorPacket []byte origPacketSize int16 packetCount int16 hasUpdates bool mutex sync.RWMutex // Thread safety for skills/nameMap updatesMutex sync.Mutex // Thread safety for updates bonusMutex sync.RWMutex // Thread safety for bonuses } // NewPlayerSkillList creates a new player skill list func NewPlayerSkillList() *PlayerSkillList { return &PlayerSkillList{ skills: make(map[int32]*Skill), nameSkillMap: make(map[string]*Skill), skillUpdates: make([]*Skill, 0), skillBonusList: make(map[int32]*SkillBonus), hasUpdates: false, } } // AddSkill adds a skill to the player's skill list func (psl *PlayerSkillList) AddSkill(newSkill *Skill) { if newSkill == nil { return } psl.mutex.Lock() defer psl.mutex.Unlock() // Remove old skill if it exists if oldSkill, exists := psl.skills[newSkill.SkillID]; exists { // TODO: Set Lua user data stale when LuaInterface is integrated _ = oldSkill } psl.skills[newSkill.SkillID] = newSkill // Clear name map cache so it gets rebuilt psl.nameSkillMap = make(map[string]*Skill) } // RemoveSkill removes a skill from the player's skill list func (psl *PlayerSkillList) RemoveSkill(skill *Skill) { if skill == nil { return } psl.mutex.Lock() defer psl.mutex.Unlock() // TODO: Set Lua user data stale when LuaInterface is integrated skill.ActiveSkill = false // Clear name map cache psl.nameSkillMap = make(map[string]*Skill) } // GetAllSkills returns all player skills func (psl *PlayerSkillList) GetAllSkills() map[int32]*Skill { psl.mutex.RLock() defer psl.mutex.RUnlock() // Return a copy to prevent external modification skills := make(map[int32]*Skill) for id, skill := range psl.skills { skills[id] = skill } return skills } // HasSkill checks if player has a specific skill func (psl *PlayerSkillList) HasSkill(skillID int32) bool { psl.mutex.RLock() defer psl.mutex.RUnlock() skill, exists := psl.skills[skillID] return exists && skill.ActiveSkill } // GetSkill returns a skill by ID func (psl *PlayerSkillList) GetSkill(skillID int32) *Skill { psl.mutex.RLock() defer psl.mutex.RUnlock() if skill, exists := psl.skills[skillID]; exists && skill.ActiveSkill { return skill } return nil } // GetSkillByName returns a skill by name func (psl *PlayerSkillList) GetSkillByName(name string) *Skill { psl.mutex.Lock() defer psl.mutex.Unlock() // Build name map if empty if len(psl.nameSkillMap) == 0 { for _, skill := range psl.skills { if skill.ActiveSkill { psl.nameSkillMap[skill.Name.Data] = skill } } } if skill, exists := psl.nameSkillMap[name]; exists { return skill } return nil } // IncreaseSkill increases a skill's current value func (psl *PlayerSkillList) IncreaseSkill(skill *Skill, amount int16) { if skill == nil { return } skill.PreviousVal = skill.CurrentVal skill.CurrentVal += amount if skill.CurrentVal > skill.MaxVal { skill.MaxVal = skill.CurrentVal } psl.AddSkillUpdateNeeded(skill) skill.SaveNeeded = true } // IncreaseSkillByID increases a skill's current value by ID func (psl *PlayerSkillList) IncreaseSkillByID(skillID int32, amount int16) { skill := psl.GetSkill(skillID) psl.IncreaseSkill(skill, amount) } // DecreaseSkill decreases a skill's current value func (psl *PlayerSkillList) DecreaseSkill(skill *Skill, amount int16) { if skill == nil { return } skill.PreviousVal = skill.CurrentVal if skill.CurrentVal < amount { skill.CurrentVal = 0 } else { skill.CurrentVal -= amount } skill.SaveNeeded = true psl.AddSkillUpdateNeeded(skill) } // DecreaseSkillByID decreases a skill's current value by ID func (psl *PlayerSkillList) DecreaseSkillByID(skillID int32, amount int16) { skill := psl.GetSkill(skillID) psl.DecreaseSkill(skill, amount) } // SetSkill sets a skill's current value func (psl *PlayerSkillList) SetSkill(skill *Skill, value int16, sendUpdate bool) { if skill == nil { return } skill.PreviousVal = skill.CurrentVal skill.CurrentVal = value if skill.CurrentVal > skill.MaxVal { skill.MaxVal = skill.CurrentVal } skill.SaveNeeded = true if sendUpdate { psl.AddSkillUpdateNeeded(skill) } } // SetSkillByID sets a skill's current value by ID func (psl *PlayerSkillList) SetSkillByID(skillID int32, value int16, sendUpdate bool) { skill := psl.GetSkill(skillID) psl.SetSkill(skill, value, sendUpdate) } // IncreaseSkillCap increases a skill's maximum value func (psl *PlayerSkillList) IncreaseSkillCap(skill *Skill, amount int16) { if skill == nil { return } skill.MaxVal += amount skill.SaveNeeded = true } // IncreaseSkillCapByID increases a skill's maximum value by ID func (psl *PlayerSkillList) IncreaseSkillCapByID(skillID int32, amount int16) { skill := psl.GetSkill(skillID) psl.IncreaseSkillCap(skill, amount) } // DecreaseSkillCap decreases a skill's maximum value func (psl *PlayerSkillList) DecreaseSkillCap(skill *Skill, amount int16) { if skill == nil { return } if skill.MaxVal < amount { skill.MaxVal = 0 } else { skill.MaxVal -= amount } // Adjust current value if it exceeds new max if skill.CurrentVal > skill.MaxVal { skill.PreviousVal = skill.CurrentVal skill.CurrentVal = skill.MaxVal } psl.AddSkillUpdateNeeded(skill) skill.SaveNeeded = true } // DecreaseSkillCapByID decreases a skill's maximum value by ID func (psl *PlayerSkillList) DecreaseSkillCapByID(skillID int32, amount int16) { skill := psl.GetSkill(skillID) psl.DecreaseSkillCap(skill, amount) } // SetSkillCap sets a skill's maximum value func (psl *PlayerSkillList) SetSkillCap(skill *Skill, value int16) { if skill == nil { return } skill.MaxVal = value // Adjust current value if it exceeds new max if skill.CurrentVal > skill.MaxVal { skill.PreviousVal = skill.CurrentVal skill.CurrentVal = skill.MaxVal } psl.AddSkillUpdateNeeded(skill) skill.SaveNeeded = true } // SetSkillCapByID sets a skill's maximum value by ID func (psl *PlayerSkillList) SetSkillCapByID(skillID int32, value int16) { skill := psl.GetSkill(skillID) psl.SetSkillCap(skill, value) } // SetSkillValuesByType sets all skills of a type to a specific value func (psl *PlayerSkillList) SetSkillValuesByType(skillType int8, value int16, sendUpdate bool) { psl.mutex.RLock() defer psl.mutex.RUnlock() for _, skill := range psl.skills { if skill != nil && skill.SkillType == int32(skillType) { psl.SetSkill(skill, value, sendUpdate) } } } // SetSkillCapsByType sets all skill caps of a type to a specific value func (psl *PlayerSkillList) SetSkillCapsByType(skillType int8, value int16) { psl.mutex.RLock() defer psl.mutex.RUnlock() for _, skill := range psl.skills { if skill != nil && skill.SkillType == int32(skillType) { psl.SetSkillCap(skill, value) } } } // IncreaseSkillCapsByType increases all skill caps of a type func (psl *PlayerSkillList) IncreaseSkillCapsByType(skillType int8, value int16) { psl.mutex.RLock() defer psl.mutex.RUnlock() for _, skill := range psl.skills { if skill != nil && skill.SkillType == int32(skillType) { psl.IncreaseSkillCap(skill, value) } } } // IncreaseAllSkillCaps increases all skill caps func (psl *PlayerSkillList) IncreaseAllSkillCaps(value int16) { psl.mutex.RLock() defer psl.mutex.RUnlock() for _, skill := range psl.skills { if skill != nil { psl.IncreaseSkillCap(skill, value) } } } // CheckSkillIncrease checks if a skill should increase and does so if successful func (psl *PlayerSkillList) CheckSkillIncrease(skill *Skill) bool { if skill == nil || skill.CurrentVal >= skill.MaxVal { return false } // Calculate increase chance: skill level 1 = 20%, 100 = 10%, 400 = 4% percent := int8((100.0 / float32(50+skill.CurrentVal)) * 10.0) if rand.Intn(100) < int(percent) { psl.IncreaseSkill(skill, 1) return true } return false } // AddSkillUpdateNeeded marks a skill as needing an update packet func (psl *PlayerSkillList) AddSkillUpdateNeeded(skill *Skill) { if skill == nil { return } psl.updatesMutex.Lock() defer psl.updatesMutex.Unlock() psl.skillUpdates = append(psl.skillUpdates, skill) psl.hasUpdates = true } // HasSkillUpdates returns whether there are pending skill updates func (psl *PlayerSkillList) HasSkillUpdates() bool { psl.updatesMutex.Lock() defer psl.updatesMutex.Unlock() return psl.hasUpdates } // GetSkillUpdates returns and clears pending skill updates func (psl *PlayerSkillList) GetSkillUpdates() []*Skill { psl.updatesMutex.Lock() defer psl.updatesMutex.Unlock() if len(psl.skillUpdates) == 0 { return nil } updates := make([]*Skill, len(psl.skillUpdates)) copy(updates, psl.skillUpdates) // Clear the updates psl.skillUpdates = psl.skillUpdates[:0] psl.hasUpdates = false return updates } // GetSaveNeededSkills returns skills that need to be saved to database func (psl *PlayerSkillList) GetSaveNeededSkills() []*Skill { psl.mutex.RLock() defer psl.mutex.RUnlock() var saveNeeded []*Skill for _, skill := range psl.skills { if skill.SaveNeeded { saveNeeded = append(saveNeeded, skill) skill.SaveNeeded = false // Clear the flag } } return saveNeeded } // ResetPackets clears cached packet data func (psl *PlayerSkillList) ResetPackets() { psl.updatesMutex.Lock() defer psl.updatesMutex.Unlock() psl.origPacket = nil psl.xorPacket = nil psl.origPacketSize = 0 psl.packetCount = 0 } // GetSkillPacket builds a skill update packet for the client func (psl *PlayerSkillList) GetSkillPacket(version int16) ([]byte, error) { psl.mutex.Lock() defer psl.mutex.Unlock() // This is a placeholder implementation // In the full implementation, this would use the PacketStruct system // to build a WS_UpdateSkillBook packet with all player skills // TODO: Implement packet building using PacketStruct system // packet := configReader.getStruct("WS_UpdateSkillBook", version) // [complex packet building logic here] // For now, return empty packet return make([]byte, 0), nil }