eq2go/internal/player/spell_management.go

624 lines
15 KiB
Go

package player
import (
"sort"
"eq2emu/internal/spells"
)
// AddSpellBookEntry adds a spell to the player's spell book
func (p *Player) AddSpellBookEntry(spellID int32, tier int8, slot int32, spellType int32, timer int32, saveNeeded bool) {
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
// Check if spell already exists
for _, entry := range p.spells {
if entry.SpellID == spellID && entry.Tier == tier {
// Update existing entry
entry.Slot = slot
entry.Type = spellType
entry.Timer = timer
entry.SaveNeeded = saveNeeded
return
}
}
// Create new entry
entry := &SpellBookEntry{
SpellID: spellID,
Tier: tier,
Slot: slot,
Type: spellType,
Timer: timer,
SaveNeeded: saveNeeded,
Player: p,
Visible: true,
InUse: false,
}
p.spells = append(p.spells, entry)
}
// GetSpellBookSpell returns a spell book entry by spell ID
func (p *Player) GetSpellBookSpell(spellID int32) *SpellBookEntry {
p.spellsBookMutex.RLock()
defer p.spellsBookMutex.RUnlock()
for _, entry := range p.spells {
if entry.SpellID == spellID {
return entry
}
}
return nil
}
// GetSpellsSaveNeeded returns spells that need saving to database
func (p *Player) GetSpellsSaveNeeded() []*SpellBookEntry {
p.spellsBookMutex.RLock()
defer p.spellsBookMutex.RUnlock()
var needSave []*SpellBookEntry
for _, entry := range p.spells {
if entry.SaveNeeded {
needSave = append(needSave, entry)
}
}
return needSave
}
// GetFreeSpellBookSlot returns the next free spell book slot for a type
func (p *Player) GetFreeSpellBookSlot(spellType int32) int32 {
p.spellsBookMutex.RLock()
defer p.spellsBookMutex.RUnlock()
// Find highest slot for this type
var maxSlot int32 = -1
for _, entry := range p.spells {
if entry.Type == spellType && entry.Slot > maxSlot {
maxSlot = entry.Slot
}
}
return maxSlot + 1
}
// GetSpellBookSpellIDBySkill returns spell IDs for a given skill
func (p *Player) GetSpellBookSpellIDBySkill(skillID int32) []int32 {
p.spellsBookMutex.RLock()
defer p.spellsBookMutex.RUnlock()
var spellIDs []int32
for _, entry := range p.spells {
// TODO: Check if spell matches skill
// spell := master_spell_list.GetSpell(entry.SpellID)
// if spell != nil && spell.GetSkillID() == skillID {
// spellIDs = append(spellIDs, entry.SpellID)
// }
}
return spellIDs
}
// HasSpell checks if player has a spell
func (p *Player) HasSpell(spellID int32, tier int8, includeHigherTiers bool, includePossibleScribe bool) bool {
p.spellsBookMutex.RLock()
defer p.spellsBookMutex.RUnlock()
for _, entry := range p.spells {
if entry.SpellID == spellID {
if tier == 255 || entry.Tier == tier {
return true
}
if includeHigherTiers && entry.Tier > tier {
return true
}
}
}
if includePossibleScribe {
// TODO: Check if player can scribe this spell
}
return false
}
// GetSpellTier returns the tier of a spell the player has
func (p *Player) GetSpellTier(spellID int32) int8 {
p.spellsBookMutex.RLock()
defer p.spellsBookMutex.RUnlock()
var highestTier int8 = 0
for _, entry := range p.spells {
if entry.SpellID == spellID && entry.Tier > highestTier {
highestTier = entry.Tier
}
}
return highestTier
}
// GetSpellSlot returns the slot of a spell
func (p *Player) GetSpellSlot(spellID int32) int8 {
entry := p.GetSpellBookSpell(spellID)
if entry != nil {
return int8(entry.Slot)
}
return -1
}
// SetSpellStatus sets the status of a spell
func (p *Player) SetSpellStatus(spell *spells.Spell, status int8) {
if spell == nil {
return
}
entry := p.GetSpellBookSpell(spell.GetSpellID())
if entry != nil {
p.AddSpellStatus(entry, int16(status), true, 0)
}
}
// RemoveSpellStatus removes a status from a spell
func (p *Player) RemoveSpellStatus(spell *spells.Spell, status int8) {
if spell == nil {
return
}
entry := p.GetSpellBookSpell(spell.GetSpellID())
if entry != nil {
p.RemoveSpellStatusEntry(entry, int16(status), true, 0)
}
}
// AddSpellStatus adds a status to a spell entry
func (p *Player) AddSpellStatus(spell *SpellBookEntry, value int16, modifyRecast bool, recast int16) {
if spell == nil {
return
}
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
spell.Status |= int8(value)
if modifyRecast {
spell.Recast = recast
spell.RecastAvailable = 0 // TODO: Calculate actual time
}
}
// RemoveSpellStatusEntry removes a status from a spell entry
func (p *Player) RemoveSpellStatusEntry(spell *SpellBookEntry, value int16, modifyRecast bool, recast int16) {
if spell == nil {
return
}
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
spell.Status &= ^int8(value)
if modifyRecast {
spell.Recast = recast
spell.RecastAvailable = 0
}
}
// RemoveSpellBookEntry removes a spell from the spell book
func (p *Player) RemoveSpellBookEntry(spellID int32, removePassivesFromList bool) {
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
for i, entry := range p.spells {
if entry.SpellID == spellID {
// Remove from slice
p.spells = append(p.spells[:i], p.spells[i+1:]...)
if removePassivesFromList {
// TODO: Remove from passive list
p.RemovePassive(spellID, entry.Tier, true)
}
break
}
}
}
// DeleteSpellBook deletes spells from the spell book based on type
func (p *Player) DeleteSpellBook(typeSelection int8) {
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
var keep []*SpellBookEntry
for _, entry := range p.spells {
deleteIt := false
// Check type flags
if typeSelection&DELETE_TRADESKILLS != 0 {
// TODO: Check if tradeskill spell
}
if typeSelection&DELETE_SPELLS != 0 {
// TODO: Check if spell
}
if typeSelection&DELETE_COMBAT_ART != 0 {
// TODO: Check if combat art
}
if typeSelection&DELETE_ABILITY != 0 {
// TODO: Check if ability
}
if typeSelection&DELETE_NOT_SHOWN != 0 && !entry.Visible {
deleteIt = true
}
if !deleteIt {
keep = append(keep, entry)
}
}
p.spells = keep
}
// ResortSpellBook resorts the spell book
func (p *Player) ResortSpellBook(sortBy, order, pattern, maxlvlOnly, bookType int32) {
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
// Filter spells based on criteria
var filtered []*SpellBookEntry
for _, entry := range p.spells {
// TODO: Apply filters based on pattern, maxlvlOnly, bookType
filtered = append(filtered, entry)
}
// Sort based on sortBy and order
switch sortBy {
case 0: // By name
if order == 0 {
sort.Slice(filtered, func(i, j int) bool {
return SortSpellEntryByName(filtered[i], filtered[j])
})
} else {
sort.Slice(filtered, func(i, j int) bool {
return SortSpellEntryByNameReverse(filtered[i], filtered[j])
})
}
case 1: // By level
if order == 0 {
sort.Slice(filtered, func(i, j int) bool {
return SortSpellEntryByLevel(filtered[i], filtered[j])
})
} else {
sort.Slice(filtered, func(i, j int) bool {
return SortSpellEntryByLevelReverse(filtered[i], filtered[j])
})
}
case 2: // By category
if order == 0 {
sort.Slice(filtered, func(i, j int) bool {
return SortSpellEntryByCategory(filtered[i], filtered[j])
})
} else {
sort.Slice(filtered, func(i, j int) bool {
return SortSpellEntryByCategoryReverse(filtered[i], filtered[j])
})
}
}
// Reassign slots
for i, entry := range filtered {
entry.Slot = int32(i)
}
}
// Spell sorting functions
func SortSpellEntryByName(s1, s2 *SpellBookEntry) bool {
// TODO: Get spell names and compare
return s1.SpellID < s2.SpellID
}
func SortSpellEntryByNameReverse(s1, s2 *SpellBookEntry) bool {
return !SortSpellEntryByName(s1, s2)
}
func SortSpellEntryByLevel(s1, s2 *SpellBookEntry) bool {
// TODO: Get spell levels and compare
return s1.Tier < s2.Tier
}
func SortSpellEntryByLevelReverse(s1, s2 *SpellBookEntry) bool {
return !SortSpellEntryByLevel(s1, s2)
}
func SortSpellEntryByCategory(s1, s2 *SpellBookEntry) bool {
// TODO: Get spell categories and compare
return s1.Type < s2.Type
}
func SortSpellEntryByCategoryReverse(s1, s2 *SpellBookEntry) bool {
return !SortSpellEntryByCategory(s1, s2)
}
// LockAllSpells locks all non-tradeskill spells
func (p *Player) LockAllSpells() {
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
p.allSpellsLocked = true
for _, entry := range p.spells {
// TODO: Check if not tradeskill spell
entry.Status |= SPELL_STATUS_LOCK
}
}
// UnlockAllSpells unlocks all non-tradeskill spells
func (p *Player) UnlockAllSpells(modifyRecast bool, exception *spells.Spell) {
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
p.allSpellsLocked = false
exceptionID := int32(0)
if exception != nil {
exceptionID = exception.GetSpellID()
}
for _, entry := range p.spells {
if entry.SpellID != exceptionID {
// TODO: Check if not tradeskill spell
entry.Status &= ^SPELL_STATUS_LOCK
if modifyRecast {
entry.RecastAvailable = 0
}
}
}
}
// LockSpell locks a spell and all linked spells
func (p *Player) LockSpell(spell *spells.Spell, recast int16) {
if spell == nil {
return
}
// Lock the main spell
entry := p.GetSpellBookSpell(spell.GetSpellID())
if entry != nil {
p.AddSpellStatus(entry, SPELL_STATUS_LOCK, true, recast)
}
// TODO: Lock all spells with shared timer
}
// UnlockSpell unlocks a spell and all linked spells
func (p *Player) UnlockSpell(spell *spells.Spell) {
if spell == nil {
return
}
p.UnlockSpellByID(spell.GetSpellID(), spell.GetSpellData().LinkedTimerID)
}
// UnlockSpellByID unlocks a spell by ID
func (p *Player) UnlockSpellByID(spellID, linkedTimerID int32) {
// Unlock the main spell
entry := p.GetSpellBookSpell(spellID)
if entry != nil {
p.RemoveSpellStatusEntry(entry, SPELL_STATUS_LOCK, true, 0)
}
// TODO: Unlock all spells with shared timer
if linkedTimerID > 0 {
// Get all spells with this timer and unlock them
}
}
// LockTSSpells locks tradeskill spells and unlocks combat spells
func (p *Player) LockTSSpells() {
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
for _, entry := range p.spells {
// TODO: Check if tradeskill spell
// if spell.IsTradeskill() {
// entry.Status |= SPELL_STATUS_LOCK
// } else {
// entry.Status &= ^SPELL_STATUS_LOCK
// }
}
}
// UnlockTSSpells unlocks tradeskill spells and locks combat spells
func (p *Player) UnlockTSSpells() {
p.spellsBookMutex.Lock()
defer p.spellsBookMutex.Unlock()
for _, entry := range p.spells {
// TODO: Check if tradeskill spell
// if spell.IsTradeskill() {
// entry.Status &= ^SPELL_STATUS_LOCK
// } else {
// entry.Status |= SPELL_STATUS_LOCK
// }
}
}
// QueueSpell queues a spell for casting
func (p *Player) QueueSpell(spell *spells.Spell) {
if spell == nil {
return
}
entry := p.GetSpellBookSpell(spell.GetSpellID())
if entry != nil {
p.AddSpellStatus(entry, SPELL_STATUS_QUEUE, false, 0)
}
}
// UnQueueSpell removes a spell from the queue
func (p *Player) UnQueueSpell(spell *spells.Spell) {
if spell == nil {
return
}
entry := p.GetSpellBookSpell(spell.GetSpellID())
if entry != nil {
p.RemoveSpellStatusEntry(entry, SPELL_STATUS_QUEUE, false, 0)
}
}
// GetSpellBookSpellsByTimer returns all spells with a given timer
func (p *Player) GetSpellBookSpellsByTimer(spell *spells.Spell, timerID int32) []*spells.Spell {
var timerSpells []*spells.Spell
p.spellsBookMutex.RLock()
defer p.spellsBookMutex.RUnlock()
// TODO: Find all spells with matching timer
// for _, entry := range p.spells {
// spell := master_spell_list.GetSpell(entry.SpellID)
// if spell != nil && spell.GetTimerID() == timerID {
// timerSpells = append(timerSpells, spell)
// }
// }
return timerSpells
}
// AddPassiveSpell adds a passive spell
func (p *Player) AddPassiveSpell(id int32, tier int8) {
for _, spellID := range p.passiveSpells {
if spellID == id {
return // Already have it
}
}
p.passiveSpells = append(p.passiveSpells, id)
}
// RemovePassive removes a passive spell
func (p *Player) RemovePassive(id int32, tier int8, removeFromList bool) {
// TODO: Remove passive effects
if removeFromList {
for i, spellID := range p.passiveSpells {
if spellID == id {
p.passiveSpells = append(p.passiveSpells[:i], p.passiveSpells[i+1:]...)
break
}
}
}
}
// ApplyPassiveSpells applies all passive spells
func (p *Player) ApplyPassiveSpells() {
// TODO: Cast all passive spells
for _, spellID := range p.passiveSpells {
// Get spell and cast it
}
}
// RemoveAllPassives removes all passive spell effects
func (p *Player) RemoveAllPassives() {
// TODO: Remove all passive effects
p.passiveSpells = nil
}
// GetSpellSlotMappingCount returns the number of spell slots
func (p *Player) GetSpellSlotMappingCount() int16 {
p.spellsBookMutex.RLock()
defer p.spellsBookMutex.RUnlock()
return int16(len(p.spells))
}
// GetSpellPacketCount returns the spell packet count
func (p *Player) GetSpellPacketCount() int16 {
return p.spellCount
}
// AddMaintainedSpell adds a maintained spell effect
func (p *Player) AddMaintainedSpell(luaSpell *spells.LuaSpell) {
// TODO: Add to maintained effects
}
// RemoveMaintainedSpell removes a maintained spell effect
func (p *Player) RemoveMaintainedSpell(luaSpell *spells.LuaSpell) {
// TODO: Remove from maintained effects
}
// AddSpellEffect adds a spell effect
func (p *Player) AddSpellEffect(luaSpell *spells.LuaSpell, overrideExpireTime int32) {
// TODO: Add spell effect
}
// RemoveSpellEffect removes a spell effect
func (p *Player) RemoveSpellEffect(luaSpell *spells.LuaSpell) {
// TODO: Remove spell effect
}
// GetFreeMaintainedSpellSlot returns a free maintained spell slot
func (p *Player) GetFreeMaintainedSpellSlot() *spells.MaintainedEffects {
// TODO: Find free slot in maintained effects
return nil
}
// GetMaintainedSpell returns a maintained spell by ID
func (p *Player) GetMaintainedSpell(id int32, onCharLoad bool) *spells.MaintainedEffects {
// TODO: Find maintained spell
return nil
}
// GetMaintainedSpellBySlot returns a maintained spell by slot
func (p *Player) GetMaintainedSpellBySlot(slot int8) *spells.MaintainedEffects {
// TODO: Find maintained spell by slot
return nil
}
// GetMaintainedSpells returns all maintained spells
func (p *Player) GetMaintainedSpells() *spells.MaintainedEffects {
// TODO: Return maintained effects array
return nil
}
// GetFreeSpellEffectSlot returns a free spell effect slot
func (p *Player) GetFreeSpellEffectSlot() *spells.SpellEffects {
// TODO: Find free slot in spell effects
return nil
}
// GetSpellEffects returns all spell effects
func (p *Player) GetSpellEffects() *spells.SpellEffects {
// TODO: Return spell effects array
return nil
}
// SaveSpellEffects saves spell effects to database
func (p *Player) SaveSpellEffects() {
if p.stopSaveSpellEffects {
return
}
// TODO: Save spell effects to database
}
// GetTierUp returns the next tier for a given tier
func (p *Player) GetTierUp(tier int16) int16 {
switch tier {
case 0:
return 1
case 1:
return 2
case 2:
return 3
case 3:
return 4
case 4:
return 5
case 5:
return 6
case 6:
return 7
case 7:
return 8
case 8:
return 9
case 9:
return 10
default:
return tier + 1
}
}