624 lines
15 KiB
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 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 == 127 || entry.Tier == tier { // Changed from 255 to avoid int8 overflow
|
|
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(), GetSpellLinkedTimerID(spell.GetSpellData()))
|
|
}
|
|
|
|
// 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 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 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 range p.passiveSpells {
|
|
// TODO: 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
|
|
}
|
|
}
|