420 lines
10 KiB
Go
420 lines
10 KiB
Go
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
|
|
} |