426 lines
19 KiB
C++
426 lines
19 KiB
C++
/*
|
|
EQ2Emulator: Everquest II Server Emulator
|
|
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
|
|
|
This file is part of EQ2Emulator.
|
|
|
|
EQ2Emulator is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
EQ2Emulator is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef __EQ2_SPELL_PROCESS__
|
|
#define __EQ2_SPELL_PROCESS__
|
|
#include <mutex>
|
|
#include <shared_mutex>
|
|
|
|
#include "client.h"
|
|
#include "Spells.h"
|
|
#include "zoneserver.h"
|
|
#include "LuaInterface.h"
|
|
#include "MutexMap.h"
|
|
#include "MutexList.h"
|
|
#include "World.h"
|
|
#include "HeroicOp/HeroicOp.h"
|
|
|
|
#define MODIFY_HEALTH 1
|
|
#define MODIFY_FOCUS 2
|
|
#define MODIFY_DEFENSE 3
|
|
#define MODIFY_POWER 4
|
|
#define MODIFY_SPEED 5
|
|
#define MODIFY_INT 6
|
|
#define MODIFY_WIS 7
|
|
#define MODIFY_STR 8
|
|
#define MODIFY_AGI 9
|
|
#define MODIFY_STA 10
|
|
#define MODIFY_COLD_RESIST 11
|
|
#define MODIFY_HEAT_RESIST 12
|
|
#define MODIFY_DISEASE_RESIST 13
|
|
#define MODIFY_POISON_RESIST 14
|
|
#define MODIFY_MAGIC_RESIST 15
|
|
#define MODIFY_MENTAL_RESIST 16
|
|
#define MODIFY_DIVINE_RESIST 17
|
|
#define MODIFY_ATTACK 18
|
|
#define MODIFY_MITIGATION 19
|
|
#define MODIFY_AVOIDANCE 20
|
|
#define MODIFY_CONCENTRATION 21
|
|
#define MODIFY_EXP 22
|
|
#define MODIFY_FACTION 23
|
|
#define CHANGE_SIZE 24
|
|
#define CHANGE_RACE 25
|
|
#define CHANGE_LOCATION 26
|
|
#define CHANGE_ZONE 27
|
|
#define CHANGE_PREFIX_TITLE 28
|
|
#define CHANGE_DEITY 29
|
|
#define CHANGE_LAST_NAME 30
|
|
#define MODIFY_HASTE 31
|
|
#define MODIFY_SKILL 32
|
|
#define CHANGE_TARGET 33
|
|
#define CHANGE_LEVEL 34
|
|
#define MODIFY_SPELL_CAST_TIME 35
|
|
#define MODIFY_SPELL_POWER_REQ 36
|
|
#define MODIFY_SPELL_HEALTH_REQ 37
|
|
#define MODIFY_SPELL_RECOVERY 38
|
|
#define MODIFY_SPELL_RECAST_TIME 39
|
|
#define MODIFY_SPELL_RADIUS 40
|
|
#define MODIFY_SPELL_AOE_TARGETS 41
|
|
#define MODIFY_SPELL_RANGE 42
|
|
#define MODIFY_SPELL_DURATION 43
|
|
#define MODIFY_SPELL_RESISTIBILITY 44
|
|
#define MODIFY_DAMAGE 45
|
|
#define MODIFY_DELAY 46
|
|
#define MODIFY_TRADESKILL_EXP 47
|
|
#define ADD_MOUNT 48
|
|
#define REMOVE_MOUNT 49
|
|
#define MODIFY_SPELL_CRIT_CHANCE 50
|
|
#define MODIFY_CRIT_CHANCE 51
|
|
#define SUMMON_ITEM 52
|
|
#define MODIFY_JUMP 53
|
|
#define MODIFY_FALL_SPEED 54
|
|
#define INFLICT_DAMAGE 55
|
|
#define ADD_DOT 56
|
|
#define REMOVE_DOT 57
|
|
#define HEAL_TARGET 58
|
|
#define HEAL_AOE 59
|
|
#define INFLICT_AOE_DAMAGE 60
|
|
#define HEAL_GROUP_AOE 61
|
|
#define ADD_AOE_DOT 62
|
|
#define REMOVE_AOE_DOT 63
|
|
#define ADD_HOT 64
|
|
#define REMOVE_HOT 65
|
|
#define MODIFY_AGGRO_RANGE 66
|
|
#define BLIND_TARGET 67
|
|
#define UNBLIND_TARGET 68
|
|
#define KILL_TARGET 69
|
|
#define RESURRECT_TARGET 70
|
|
#define CHANGE_SUFFIX_TITLE 71
|
|
#define SUMMON_PET 72
|
|
#define MODIFY_HATE 73
|
|
#define ADD_REACTIVE_HEAL 74
|
|
#define MODIFY_POWER_REGEN 75
|
|
#define MODIFY_HP_REGEN 76
|
|
#define FEIGN_DEATH 77
|
|
#define MODIFY_VISION 78
|
|
#define INVISIBILITY 79
|
|
#define CHARM_TARGET 80
|
|
#define MODIFY_TRADESKILL_DURABILITY 81
|
|
#define MODIFY_TRADESKILL_PROGRESS 82
|
|
|
|
|
|
#define ACTIVE_SPELL_NORMAL 0
|
|
#define ACTIVE_SPELL_ADD 1
|
|
#define ACTIVE_SPELL_REMOVE 2
|
|
|
|
#define GET_VALUE_BAD_VALUE 0xFFFFFFFF
|
|
struct InterruptStruct{
|
|
Spawn* interrupted;
|
|
LuaSpell* spell;
|
|
int16 error_code;
|
|
};
|
|
struct CastTimer{
|
|
Client* caster;
|
|
int32 target_id;
|
|
EntityCommand* entity_command;
|
|
LuaSpell* spell;
|
|
Timer* timer;
|
|
ZoneServer* zone;
|
|
bool delete_timer;
|
|
bool in_heroic_opp;
|
|
};
|
|
struct CastSpell{
|
|
Entity* caster;
|
|
Spawn* target;
|
|
int32 spell_id;
|
|
ZoneServer* zone;
|
|
};
|
|
struct RecastTimer{
|
|
Entity* caster;
|
|
Client* client;
|
|
Spell* spell;
|
|
Timer* timer;
|
|
int32 spell_id;
|
|
int32 linked_timer;
|
|
int32 type_group_spell_id;
|
|
bool stay_locked;
|
|
};
|
|
|
|
/// <summary> Handles all spell casts for a zone, only 1 SpellProcess per zone </summary>
|
|
class SpellProcess{
|
|
public:
|
|
SpellProcess();
|
|
~SpellProcess();
|
|
|
|
/// Remove dead pointers for casters when the Spawn is deconstructed
|
|
void RemoveCaster(Spawn* caster, bool lock_spell_process = true);
|
|
|
|
/// <summary>Remove all spells from the SpellProcess </summary>
|
|
void RemoveAllSpells(bool reload_spells = false);
|
|
|
|
/// <summary>Main loop, handles everything (interupts, cast time, recast, ...) </summary>
|
|
void Process();
|
|
|
|
/// <summary>Interrupts the caster (creates the InterruptStruct and adds it to a list)</summary>
|
|
/// <param name='caster'>Entity being interrupted</param>
|
|
/// <param name='interruptor'>Spawn that interrupted the caster</param>
|
|
/// <param name='error_code'>The error code</param>
|
|
/// <param name='cancel'>Bool if the spell was cancelled not interrupted</param>
|
|
void Interrupted(Entity* caster, Spawn* interruptor, int16 error_code, bool cancel = false, bool from_movement = false);
|
|
|
|
/// <summary>Does all the checks and actually casts the spell</summary>
|
|
/// <param name='zone'>The current ZoneServer</param>
|
|
/// <param name='spell'>The Spell to cast</param>
|
|
/// <param name='caster'>The Entity casting the spell</param>
|
|
/// <param name='target'>The target(Spawn) of the spell</param>
|
|
/// <param name='lock'>??? not currently used</param>
|
|
/// <param name='harvest_spell'>Is this a harvest spell?</param>
|
|
void ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target = 0, bool lock = true, bool harvest_spell = false, LuaSpell* customSpell = 0, int16 custom_cast_time = 0, bool in_heroic_opp = false);
|
|
|
|
/// <summary>Cast an EntityCommand (right click menu)</summary>
|
|
/// <param name='zone'>The current ZoneServer</param>
|
|
/// <param name='entity_command'>the EntityCommand to cast</param>
|
|
/// <param name='caster'>The Entity casting the EntityCommand</param>
|
|
/// <param name='target'>The target(Spawn*) of the EntityCommand</param>
|
|
/// <param name='lock'>??? not currently used</param>
|
|
void ProcessEntityCommand(ZoneServer* zone, EntityCommand* entity_command, Entity* caster, Spawn* target, bool lock = true, bool in_heroic_opp = false);
|
|
|
|
/// <summary>Checks to see if the caster has enough power and takes it</summary>
|
|
/// <param name='spell'>LuaSpell to check and take power for (LuaSpell contains the caster)</param>
|
|
/// <returns>True if caster had enough power</returns>
|
|
bool TakePower(LuaSpell* spell, int32 custom_power_req = 0);
|
|
|
|
/// <summary>Check to see if the caster has enough power to cast the spell</summary>
|
|
/// <param name='spell'>LuaSpell to check (LuaSpell contains the caster)</param>
|
|
/// <returns>True if the caster has enough power</returns>
|
|
bool CheckPower(LuaSpell* spell);
|
|
|
|
/// <summary>Check to see if the caster has enough hp and take it</summary>
|
|
/// <param name='spell'>LuaSpell to check and take hp for (LuaSpell contains the caster)</param>
|
|
/// <returns>True if the caster had enough hp</returns>
|
|
bool TakeHP(LuaSpell* spell, int32 custom_hp_req = 0);
|
|
|
|
/// <summary>Check to see if the caster has enough hp to cast the spell</summary>
|
|
/// <param name='spell'>LuaSpell to check (LuaSpell contains the caster)</param>
|
|
/// <returns>True if the caster had enough hp</returns>
|
|
bool CheckHP(LuaSpell* spell);
|
|
|
|
/// <summary>Check to see if the caster has enough concentration available to cast the spell</summary>
|
|
/// <param name='spell'>LuaSpell to check (LuaSpell contains the caster)</param>
|
|
/// <returns>True if the caster has enough concentration</returns>
|
|
bool CheckConcentration(LuaSpell* spell);
|
|
|
|
bool CheckSavagery(LuaSpell* spell);
|
|
bool TakeSavagery(LuaSpell* spell);
|
|
bool CheckDissonance(LuaSpell* spell);
|
|
bool AddDissonance(LuaSpell* spell);
|
|
|
|
/// <summary>Check to see if the caster has enough concentration available and add to the casters concentration</summary>
|
|
/// <param name='spell'>LuaSpell to check (LuaSpell contains the caster)</param>
|
|
/// <returns>True of the caster had enough concentration</returns>
|
|
bool AddConcentration(LuaSpell* spell);
|
|
|
|
/// <summary>Cast the spell, calls ProcessSpell for the given LuaSpell, as well as sends the messages for the spells and calls the casted on function in the targets spawn script</summary>
|
|
/// <param name='spell'>LuaSpell to cast</param>
|
|
/// <param name='passive'>Is this a passive spell being cast?</param>
|
|
/// <returns>True if the spell was casted</returns>
|
|
bool CastProcessedSpell(LuaSpell* spell, bool passive = false, bool in_heroic_opp = false);
|
|
|
|
/// <summary>Cast the EntityCommand, calls ProcessEntityCommand for the given EntityCommand, as well as sends the messages for the command and calls the casted on function in the targets spawn script</summary>
|
|
/// <param name='entity_command'>EntityCommand to cast</param>
|
|
/// <param name='client'>Client casting the entity command</param>
|
|
/// <returns>True if the spell was casted</returns>
|
|
bool CastProcessedEntityCommand(EntityCommand* entity_command, Client* client, Spawn* target, bool in_heroic_opp = false);
|
|
|
|
/// <summary>Sends the start cast packet for the given client</summary>
|
|
/// <param name='spell'>LuaSpell being cast</param>
|
|
/// <param name='client'>The client casting the spell</param>
|
|
void SendStartCast(LuaSpell* spell, Client* client);
|
|
|
|
/// <summary>Send finish cast packet and take power/hp or add conc, also checks for quest updates</summary>
|
|
/// <param name='spell'>LuaSpell that just finished casting</param>
|
|
/// <param name='client'>Client that just finished casting, null if not a player</param>
|
|
void SendFinishedCast(LuaSpell* spell, Client* client);
|
|
|
|
/// <summary>Locks all the spells for the given client (shades them all gray)</summary>
|
|
/// <param name='client'>Client to lock the spells for</param>
|
|
void LockAllSpells(Client* client);
|
|
|
|
/// <summary>Unlock all the spells for the given client</summary>
|
|
/// <param name='client'>Client to unlock the spells for</param>
|
|
void UnlockAllSpells(Client* client, Spell* exception = 0);
|
|
|
|
/// <summary>Unlock a single spell for the given client</summary>
|
|
/// <param name='client'>The client to unlock the spell for</param>
|
|
/// <param name='spell'>The spell to unlock</param>
|
|
void UnlockSpell(Client* client, Spell* spell);
|
|
void UnlockSpell(Client* client, int32 spell_id, int32 link_timer_id);
|
|
|
|
/// <summary>Remove the given spell for the given caster from the SpellProcess</summary>
|
|
/// <param name='caster'>The spawn to remove the spell for</param>
|
|
/// <param name='spell'>The spell to remove</param>
|
|
bool DeleteCasterSpell(Spawn* caster, Spell* spell, string reason = "");
|
|
|
|
/// <summary>Remove the given spell from the ZpellProcess</summary>
|
|
/// <param name='spell'>LuaSpell to remove</param>
|
|
bool DeleteCasterSpell(LuaSpell* spell, string reason="", bool removing_all_spells = false, Spawn* remove_target = nullptr, bool zone_shutting_down = false, bool shared_lock_spell = true);
|
|
|
|
/// <summary>Interrupt the spell</summary>
|
|
/// <param name='interrupt'>InterruptStruct that contains all the info</param>
|
|
void CheckInterrupt(InterruptStruct* interrupt);
|
|
|
|
/// <summary>Removes the timers for the given spawn</summary>
|
|
/// <param name='spawn'>Spawn to remove the timers for</param>
|
|
/// <param name='remove_all'>Remove all timers (cast, recast, active, queue, interrupted)? If false only cast timers are removed</param>
|
|
void RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all = false, bool delete_recast = false, bool call_expire_function = true, bool lock_spell_process = false);
|
|
|
|
/// <summary>Sets the recast timer for the spell </summary>
|
|
/// <param name='spell'>The spell to set the recast for</param>
|
|
/// <param name='caster'>The entity to set the recast for</param>
|
|
/// <param name='timer_override'>Change the recast timer of the spell</param>
|
|
/// <param name='check_linked_timers'>Set the recast on all other spells the player has with the same timer</param>
|
|
void CheckRecast(Spell* spell, Entity* caster, float timer_override = 0, bool check_linked_timers = true);
|
|
|
|
/// <summary>Add a spell to the queue for the player</summary>
|
|
/// <param name='spell'>Spell to add</param>
|
|
/// <param name='caster'>Entity's queue to add the spell to, if not a player function does nothing</param>
|
|
void AddSpellToQueue(Spell* spell, Entity* caster);
|
|
|
|
/// <summary>Removes a spell from the queue for the player</summary>
|
|
/// <param name='spell'>Spell to remove from the queue</param>
|
|
/// <param name='caster'>Entity's queue to remove the spell from, if not a player function does nothing</param>
|
|
void RemoveSpellFromQueue(Spell* spell, Entity* caster, bool send_update = true);
|
|
|
|
/// <summary>Clear the queue, or clear only hostile spells from the queue</summary>
|
|
/// <param name='caster'>Entity to clear the queue for, if not player function does nothing</param>
|
|
/// <param name='hostile_only'>Set to true to only remove hostile spells, default is false</param>
|
|
void RemoveSpellFromQueue(Entity* caster, bool hostile_only = false);
|
|
|
|
/// <summary>Check the given enities queue for the spell, if found remove, if not found add</summary>
|
|
/// <param name='spell'>Spell to check for</param>
|
|
/// <param name='caster'>Entity's queue to check, if not player function does nothing</param>
|
|
bool CheckSpellQueue(Spell* spell, Entity* caster);
|
|
|
|
/// <summary>Checks to see if the entity can cast the spell </summary>
|
|
/// <param name='spell'>The spell being cast</param>
|
|
/// <param name='caster'>The entity casting the spell </param>
|
|
bool IsReady(Spell* spell, Entity* caster);
|
|
|
|
/// <summary>Send the spell book update packet to the given client</summary>
|
|
/// <param name='client'>Client to send the packet to</param>
|
|
void SendSpellBookUpdate(Client* client);
|
|
|
|
/// <summary>Gets the target of the currently casting spell for the given entity</summary>
|
|
/// <param name='caster'>Entity whos spell we are checking</param>
|
|
/// <returns>Spawn* - the spells target</returns>
|
|
Spawn* GetSpellTarget(Entity* caster);
|
|
|
|
/// <summary>Gets the currently casting spell for the given entity</summary>
|
|
/// <param name='caster'>Entity to get the spell for</param>
|
|
/// <returns>Spell* for the currently casting spell</returns>
|
|
Spell* GetSpell(Entity* caster);
|
|
|
|
/// <summary>Gets the currently casting LuaSpell for the given entity</summary>
|
|
/// <param name='caster'>Entity to get the LuaSpell for</param>
|
|
/// <returns>LuaSpell* for the currently casting spell</returns>
|
|
LuaSpell* GetLuaSpell(Entity* caster);
|
|
|
|
/// <summary>Gets the targets for the spell and adds them to the LuaSpell targets array</summary>
|
|
/// <param name='luaspell'>LuaSpell to get the targets for</param>
|
|
static void GetSpellTargets(LuaSpell* luaspell);
|
|
|
|
static bool GetPlayerGroupTargets(Player* target, Spawn* caster, LuaSpell* luaspell, bool bypassSpellChecks=false, bool bypassRangeChecks=false);
|
|
|
|
/// <summary>Gets targets for a true aoe spell (not an encounter ae) and adds them to the LuaSpell targets array</summary>
|
|
/// <param name='luaspell'>LuaSpell to get the targets for</param>
|
|
static void GetSpellTargetsTrueAOE(LuaSpell* luaspell);
|
|
|
|
/// <summary>Applies or removes passive spells, bypasses the spell queue and treats the spell as an insta cast spell</summary>
|
|
/// <param name='spell'>The passive spell to apply or remove</param>
|
|
/// <param name='caster'>The Entity to apply or remove the passive spell to</param>
|
|
/// <param name='remove'>Tells the function to remove the spell effects of this passive, default is false</param>
|
|
bool CastPassives(Spell* spell, Entity* caster, bool remove = false);
|
|
bool CastInstant(Spell* spell, Entity* caster, Entity* target, bool remove = false, bool passive=false);
|
|
|
|
/// <summary>Adds a spell script timer to the list</summary>
|
|
/// <param name='timer'>Timer to add</param>
|
|
void AddSpellScriptTimer(SpellScriptTimer* timer);
|
|
|
|
/// <summary>Removes a spell script timer from the list</summary>
|
|
/// <param name='timer'>Timer to remove</param>
|
|
void RemoveSpellScriptTimer(SpellScriptTimer* timer, bool locked=false);
|
|
void RemoveSpellScriptTimerBySpell(LuaSpell* spell, bool clearPendingDeletes=true);
|
|
|
|
/// <summary>Checks the spell script timers</summary>
|
|
void CheckSpellScriptTimers();
|
|
|
|
/// <summary>Checks to see if the list has the spell</summary>
|
|
bool SpellScriptTimersHasSpell(LuaSpell* spell);
|
|
std::string SpellScriptTimerCustomFunction(LuaSpell* spell);
|
|
|
|
void ClearSpellScriptTimerList();
|
|
|
|
MutexList<LuaSpell*>* GetActiveSpells() { return &active_spells; }
|
|
|
|
void RemoveTargetFromSpell(LuaSpell* spell, Spawn* target, bool remove_caster = false);
|
|
void CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete = true, bool removing_all_spells = false);
|
|
void RemoveTargetList(LuaSpell* spell);
|
|
|
|
/// <summary>Adds a solo HO to the SpellProcess</summary>
|
|
/// <param name='client'>The client who is starting the HO</param>
|
|
/// <param name='ho'>The HO that is being started</param>
|
|
bool AddHO(Client* client, HeroicOP* ho);
|
|
|
|
/// <summary>Adds a group HO to the SpellProcess</summary>
|
|
/// <param name='group_id'>ID of the group that is starting the HO</param>
|
|
/// <param name='ho'>The HO that is being started</param>
|
|
bool AddHO(int32 group_id, HeroicOP* ho);
|
|
|
|
/// <summary>Stops the HO that targets the given spawn</summary>
|
|
/// <param name='spawn_id'>ID of the spawn targeted by the HO we want to stop</param>
|
|
void KillHOBySpawnID(int32 spawn_id);
|
|
|
|
void AddSpellCancel(LuaSpell* spell);
|
|
|
|
void DeleteSpell(LuaSpell* spell);
|
|
|
|
void SpellCannotStack(ZoneServer* zone, Client* client, Entity* caster, LuaSpell* lua_spell, LuaSpell* conflictSpell);
|
|
|
|
bool ProcessSpell(LuaSpell* spell, bool first_cast = true, const char* function = 0, SpellScriptTimer* timer = 0, bool all_targets = false);
|
|
std::string ApplyLuaFunction(LuaSpell* spell, bool first_cast, const char* function, SpellScriptTimer* timer, Spawn* altTarget = 0);
|
|
|
|
void AddActiveSpell(LuaSpell* spell);
|
|
static void AddSelfAndPet(LuaSpell* spell, Spawn* self, bool onlyPet=false);
|
|
static void AddSelfAndPetToCharTargets(LuaSpell* spell, Spawn* caster, bool onlyPet=false);
|
|
void DeleteActiveSpell(LuaSpell* spell, bool skipRemoveCurrent = false);
|
|
static bool AddLuaSpellTarget(LuaSpell* lua_spell, int32 id, bool lock_spell_targets = true);
|
|
mutable std::shared_mutex MSpellProcess;
|
|
private:
|
|
MutexMap<Entity*,Spell*> spell_que;
|
|
MutexList<LuaSpell*> active_spells;
|
|
MutexList<CastTimer*> cast_timers;
|
|
MutexList<InterruptStruct*>interrupt_list;
|
|
MutexList<RecastTimer*> recast_timers;
|
|
int32 last_checked_time;
|
|
vector<SpellScriptTimer*> m_spellScriptList;
|
|
Mutex MSpellScriptTimers;
|
|
map<LuaSpell*, vector<int32>*> remove_target_list;
|
|
Mutex MRemoveTargetList;
|
|
vector<LuaSpell*> SpellCancelList;
|
|
Mutex MSpellCancelList;
|
|
|
|
Mutex MSoloHO;
|
|
Mutex MGroupHO;
|
|
map<Client*, HeroicOP*> m_soloHO;
|
|
map<int32, HeroicOP*> m_groupHO;
|
|
};
|
|
|
|
#endif
|
|
|