/* EQEMu: Everquest Server Emulator Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) This program 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; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY except by those people which sell it, which are required to give you total support for your newly bought product; 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* General outline of spell casting process 1. a) Client clicks a spell bar gem, ability, or item. client_process.cpp gets the op and calls CastSpell() with all the relevant info including cast time. b) NPC does CastSpell() from AI 2. a) CastSpell() determines there is a cast time and sets some state keeping flags to be used to check the progress of casting and finish it later. b) CastSpell() sees there's no cast time, and calls CastedSpellFinished() Go to step 4. 3. SpellProcess() notices that the spell casting timer which was set by CastSpell() is expired, and calls CastedSpellFinished() 4. CastedSpellFinished() checks some timed spell specific things, like wether to interrupt or not, due to movement or melee. If successful SpellFinished() is called. 5. SpellFinished() checks some things like LoS, reagents, target and figures out what's going to get hit by this spell based on its type. 6. a) Single target spell, SpellOnTarget() is called. b) AE spell, Entity::AESpell() is called. c) Group spell, Group::CastGroupSpell()/SpellOnTarget() is called as needed. 7. SpellOnTarget() may or may not call SpellEffect() to cause effects to the target 8. If this was timed, CastedSpellFinished() will restore the client's spell bar gems. Most user code should call CastSpell(), with a 0 casting time if needed, and not SpellFinished(). */ #include "../common/eqemu_logsys.h" #include "classes.h" #include "spdat.h" #ifndef WIN32 #include #include "unix.h" #endif /////////////////////////////////////////////////////////////////////////////// // spell property testing functions bool IsTargetableAESpell(uint16 spell_id) { if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) return true; return false; } bool IsSacrificeSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_Sacrifice); } bool IsLifetapSpell(uint16 spell_id) { // Ancient Lifebane: 2115 if (IsValidSpell(spell_id) && (spells[spell_id].targettype == ST_Tap || spells[spell_id].targettype == ST_TargetAETap || spell_id == 2115)) return true; return false; } bool IsMezSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_Mez); } bool IsStunSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_Stun); } bool IsSummonSpell(uint16 spellid) { for (int o = 0; o < EFFECT_COUNT; o++) { uint32 tid = spells[spellid].effectid[o]; if (tid == SE_SummonPet || tid == SE_SummonItem || tid == SE_SummonPC) return true; } return false; } bool IsEvacSpell(uint16 spellid) { return IsEffectInSpell(spellid, SE_Succor); } bool IsDamageSpell(uint16 spellid) { for (int o = 0; o < EFFECT_COUNT; o++) { uint32 tid = spells[spellid].effectid[o]; if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spellid].targettype != ST_Tap && spells[spellid].buffduration < 1 && spells[spellid].base[o] < 0) return true; } return false; } bool IsFearSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_Fear); } bool IsCureSpell(uint16 spell_id) { const SPDat_Spell_Struct &sp = spells[spell_id]; bool CureEffect = false; for(int i = 0; i < EFFECT_COUNT; i++){ if (sp.effectid[i] == SE_DiseaseCounter || sp.effectid[i] == SE_PoisonCounter || sp.effectid[i] == SE_CurseCounter || sp.effectid[i] == SE_CorruptionCounter) CureEffect = true; } if (CureEffect && IsBeneficialSpell(spell_id)) return true; return false; } bool IsSlowSpell(uint16 spell_id) { const SPDat_Spell_Struct &sp = spells[spell_id]; for(int i = 0; i < EFFECT_COUNT; i++) if ((sp.effectid[i] == SE_AttackSpeed && sp.base[i] < 100) || (sp.effectid[i] == SE_AttackSpeed4)) return true; return false; } bool IsHasteSpell(uint16 spell_id) { const SPDat_Spell_Struct &sp = spells[spell_id]; for(int i = 0; i < EFFECT_COUNT; i++) if(sp.effectid[i] == SE_AttackSpeed) return (sp.base[i] < 100); return false; } bool IsHarmonySpell(uint16 spell_id) { // IsEffectInSpell(spell_id, SE_Lull) - Lull is not calculated anywhere atm return (IsEffectInSpell(spell_id, SE_Harmony) || IsEffectInSpell(spell_id, SE_ChangeFrenzyRad)); } bool IsPercentalHealSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_PercentalHeal); } bool IsGroupOnlySpell(uint16 spell_id) { return IsValidSpell(spell_id) && spells[spell_id].goodEffect == 2; } bool IsBeneficialSpell(uint16 spell_id) { if (!IsValidSpell(spell_id)) return false; // You'd think just checking goodEffect flag would be enough? if (spells[spell_id].goodEffect == 1) { // If the target type is ST_Self or ST_Pet and is a SE_CancleMagic spell // it is not Beneficial SpellTargetType tt = spells[spell_id].targettype; if (tt != ST_Self && tt != ST_Pet && IsEffectInSpell(spell_id, SE_CancelMagic)) return false; // When our targettype is ST_Target, ST_AETarget, ST_Aniaml, ST_Undead, or ST_Pet // We need to check more things! if (tt == ST_Target || tt == ST_AETarget || tt == ST_Animal || tt == ST_Undead || tt == ST_Pet) { uint16 sai = spells[spell_id].SpellAffectIndex; // If the resisttype is magic and SpellAffectIndex is Calm/memblur/dispell sight // it's not beneficial if (spells[spell_id].resisttype == RESIST_MAGIC) { if (sai == SAI_Calm || sai == SAI_Dispell_Sight || sai == SAI_Memory_Blur || sai == SAI_Calm_Song) return false; } else { // If the resisttype is not magic and spell is Bind Sight or Cast Sight // It's not beneficial if (sai == SAI_Dispell_Sight && spells[spell_id].skill == 18 && !IsEffectInSpell(spell_id, SE_VoiceGraft)) return false; } } } // And finally, if goodEffect is not 0 or if it's a group spell it's beneficial return spells[spell_id].goodEffect != 0 || IsGroupSpell(spell_id); } bool IsDetrimentalSpell(uint16 spell_id) { return !IsBeneficialSpell(spell_id); } bool IsInvulnerabilitySpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_DivineAura); } bool IsCHDurationSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_CompleteHeal); } bool IsPoisonCounterSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_PoisonCounter); } bool IsDiseaseCounterSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_DiseaseCounter); } bool IsSummonItemSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_SummonItem); } bool IsSummonSkeletonSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_NecPet); } bool IsSummonPetSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_SummonPet) || IsEffectInSpell(spell_id, SE_SummonBSTPet)) return true; return false; } bool IsSummonPCSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_SummonPC); } bool IsCharmSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_Charm); } bool IsBlindSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_Blind); } bool IsEffectHitpointsSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_CurrentHP); } bool IsReduceCastTimeSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_IncreaseSpellHaste); } bool IsIncreaseDurationSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_IncreaseSpellDuration); } bool IsReduceManaSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_ReduceManaCost); } bool IsExtRangeSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_IncreaseRange); } bool IsImprovedHealingSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_ImprovedHeal); } bool IsImprovedDamageSpell(uint16 spell_id) { return IsEffectInSpell(spell_id, SE_ImprovedDamage); } bool IsAEDurationSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && (spells[spell_id].targettype == ST_AETarget || spells[spell_id].targettype == ST_UndeadAE) && spells[spell_id].AEDuration != 0) return true; return false; } bool IsPureNukeSpell(uint16 spell_id) { int i, effect_count = 0; if (!IsValidSpell(spell_id)) return false; for (i = 0; i < EFFECT_COUNT; i++) if (!IsBlankSpellEffect(spell_id, i)) effect_count++; if (effect_count == 1 && IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].buffduration == 0 && IsDamageSpell(spell_id)) return true; return false; } bool IsAENukeSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) && spells[spell_id].aoerange > 0) return true; return false; } bool IsPBAENukeSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) && spells[spell_id].aoerange > 0 && spells[spell_id].targettype == ST_AECaster) return true; return false; } bool IsAERainNukeSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) && spells[spell_id].aoerange > 0 && spells[spell_id].AEDuration > 1000) return true; return false; } bool IsPartialCapableSpell(uint16 spell_id) { if (spells[spell_id].no_partial_resist) return false; if (IsPureNukeSpell(spell_id)) return true; return false; } bool IsResistableSpell(uint16 spell_id) { // for now only detrimental spells are resistable. later on i will // add specific exceptions for the beneficial spells that are resistable if (IsDetrimentalSpell(spell_id)) return true; return false; } // checks if this spell affects your group bool IsGroupSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && (spells[spell_id].targettype == ST_AEBard || spells[spell_id].targettype == ST_Group || spells[spell_id].targettype == ST_GroupTeleport)) return true; return false; } // checks if this spell can be targeted bool IsTGBCompatibleSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && (!IsDetrimentalSpell(spell_id) && spells[spell_id].buffduration != 0 && !IsBardSong(spell_id) && !IsEffectInSpell(spell_id, SE_Illusion))) return true; return false; } bool IsBardSong(uint16 spell_id) { if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255 && !spells[spell_id].IsDisciplineBuff) return true; return false; } bool IsEffectInSpell(uint16 spellid, int effect) { int j; if (!IsValidSpell(spellid)) return false; for (j = 0; j < EFFECT_COUNT; j++) if (spells[spellid].effectid[j] == effect) return true; return false; } // arguments are spell id and the index of the effect to check. // this is used in loops that process effects inside a spell to skip // the blanks bool IsBlankSpellEffect(uint16 spellid, int effect_index) { int effect, base, formula; effect = spells[spellid].effectid[effect_index]; base = spells[spellid].base[effect_index]; formula = spells[spellid].formula[effect_index]; // SE_CHA is "spacer" // SE_Stacking* are also considered blank where this is used if (effect == SE_Blank || (effect == SE_CHA && base == 0 && formula == 100) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) return true; return false; } // checks some things about a spell id, to see if we can proceed bool IsValidSpell(uint32 spellid) { if (SPDAT_RECORDS > 0 && spellid != 0 && spellid != 1 && spellid != 0xFFFFFFFF && spellid < SPDAT_RECORDS && spells[spellid].player_1[0]) return true; return false; } // returns the lowest level of any caster which can use the spell int GetMinLevel(uint16 spell_id) { int r, min = 255; const SPDat_Spell_Struct &spell = spells[spell_id]; for (r = 0; r < PLAYER_CLASS_COUNT; r++) if (spell.classes[r] < min) min = spell.classes[r]; // if we can't cast the spell return 0 // just so it wont screw up calculations used in other areas of the code // seen 127, 254, 255 if (min >= 127) return 0; else return min; } int GetSpellLevel(uint16 spell_id, int classa) { if (classa >= PLAYER_CLASS_COUNT) return 255; const SPDat_Spell_Struct &spell = spells[spell_id]; return spell.classes[classa - 1]; } // this will find the first occurrence of effect. this is handy // for spells like mez and charm, but if the effect appears more than once // in a spell this will just give back the first one. int GetSpellEffectIndex(uint16 spell_id, int effect) { int i; if (!IsValidSpell(spell_id)) return -1; for (i = 0; i < EFFECT_COUNT; i++) if (spells[spell_id].effectid[i] == effect) return i; return -1; } // returns the level required to use the spell if that class/level // can use it, 0 otherwise // note: this isn't used by anything right now int CanUseSpell(uint16 spellid, int classa, int level) { int level_to_use; if (!IsValidSpell(spellid) || classa >= PLAYER_CLASS_COUNT) return 0; level_to_use = spells[spellid].classes[classa - 1]; if (level_to_use && level_to_use != 255 && level >= level_to_use) return level_to_use; return 0; } bool BeneficialSpell(uint16 spell_id) { if (spell_id <= 0 || spell_id >= SPDAT_RECORDS /*|| spells[spell_id].stacking == 27*/ ) return true; switch (spells[spell_id].goodEffect) { case 1: case 3: return true; } return false; } bool GroupOnlySpell(uint16 spell_id) { switch (spells[spell_id].goodEffect) { case 2: case 3: return true; } switch (spell_id) { case 1771: return true; } return false; } int32 CalculatePoisonCounters(uint16 spell_id) { if (!IsValidSpell(spell_id)) return 0; int32 Counters = 0; for (int i = 0; i < EFFECT_COUNT; i++) if (spells[spell_id].effectid[i] == SE_PoisonCounter && spells[spell_id].base[i] > 0) Counters += spells[spell_id].base[i]; return Counters; } int32 CalculateDiseaseCounters(uint16 spell_id) { if (!IsValidSpell(spell_id)) return 0; int32 Counters = 0; for (int i = 0; i < EFFECT_COUNT; i++) if(spells[spell_id].effectid[i] == SE_DiseaseCounter && spells[spell_id].base[i] > 0) Counters += spells[spell_id].base[i]; return Counters; } int32 CalculateCurseCounters(uint16 spell_id) { if (!IsValidSpell(spell_id)) return 0; int32 Counters = 0; for (int i = 0; i < EFFECT_COUNT; i++) if(spells[spell_id].effectid[i] == SE_CurseCounter && spells[spell_id].base[i] > 0) Counters += spells[spell_id].base[i]; return Counters; } int32 CalculateCorruptionCounters(uint16 spell_id) { if (!IsValidSpell(spell_id)) return 0; int32 Counters = 0; for (int i = 0; i < EFFECT_COUNT; i++) if (spells[spell_id].effectid[i] == SE_CorruptionCounter && spells[spell_id].base[i] > 0) Counters += spells[spell_id].base[i]; return Counters; } int32 CalculateCounters(uint16 spell_id) { int32 counter = CalculatePoisonCounters(spell_id); if (counter != 0) return counter; counter = CalculateDiseaseCounters(spell_id); if (counter != 0) return counter; counter = CalculateCurseCounters(spell_id); if (counter != 0) return counter; counter = CalculateCorruptionCounters(spell_id); return counter; } bool IsDisciplineBuff(uint16 spell_id) { if (!IsValidSpell(spell_id)) return false; if (spells[spell_id].mana == 0 && spells[spell_id].short_buff_box == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep) && spells[spell_id].targettype == ST_Self) return true; return false; } bool IsDiscipline(uint16 spell_id) { if (!IsValidSpell(spell_id)) return false; if (spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep)) return true; return false; } bool IsCombatSkill(uint16 spell_id) { if (!IsValidSpell(spell_id)) return false; //Check if Discipline if ((spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep))) return true; return false; } bool IsResurrectionEffects(uint16 spell_id) { // spell id 756 is Resurrection Effects spell if(IsValidSpell(spell_id) && spell_id == 756) return true; return false; } bool IsRuneSpell(uint16 spell_id) { if (IsValidSpell(spell_id)) for (int i = 0; i < EFFECT_COUNT; i++) if (spells[spell_id].effectid[i] == SE_Rune) return true; return false; } bool IsMagicRuneSpell(uint16 spell_id) { if(IsValidSpell(spell_id)) for(int i = 0; i < EFFECT_COUNT; i++) if(spells[spell_id].effectid[i] == SE_AbsorbMagicAtt) return true; return false; } bool IsManaTapSpell(uint16 spell_id) { if (IsValidSpell(spell_id)) for (int i = 0; i < EFFECT_COUNT; i++) if (spells[spell_id].effectid[i] == SE_CurrentMana && spells[spell_id].targettype == ST_Tap) return true; return false; } bool IsAllianceSpellLine(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_AddFaction)) return true; return false; } bool IsDeathSaveSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_DeathSave)) return true; return false; } // Deathsave spells with base of 1 are partial bool IsPartialDeathSaveSpell(uint16 spell_id) { if (!IsValidSpell(spell_id)) return false; for (int i = 0; i < EFFECT_COUNT; i++) if (spells[spell_id].effectid[i] == SE_DeathSave && spells[spell_id].base[i] == 1) return true; return false; } // Deathsave spells with base 2 are "full" bool IsFullDeathSaveSpell(uint16 spell_id) { if (!IsValidSpell(spell_id)) return false; for (int i = 0; i < EFFECT_COUNT; i++) if (spells[spell_id].effectid[i] == SE_DeathSave && spells[spell_id].base[i] == 2) return true; return false; } bool IsShadowStepSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_ShadowStep)) return true; return false; } bool IsSuccorSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_Succor)) return true; return false; } bool IsTeleportSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_Teleport)) return true; return false; } bool IsGateSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_Gate)) return true; return false; } bool IsPlayerIllusionSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_Illusion) && spells[spell_id].targettype == ST_Self) return true; return false; } int GetSpellEffectDescNum(uint16 spell_id) { if (IsValidSpell(spell_id)) return spells[spell_id].effectdescnum; return -1; } DmgShieldType GetDamageShieldType(uint16 spell_id, int32 DSType) { // If we have a DamageShieldType for this spell from the damageshieldtypes table, return that, // else, make a guess, based on the resist type. Default return value is DS_THORNS if (IsValidSpell(spell_id)) { Log.Out(Logs::Detail, Logs::Spells, "DamageShieldType for spell %i (%s) is %X\n", spell_id, spells[spell_id].name, spells[spell_id].DamageShieldType); if (spells[spell_id].DamageShieldType) return (DmgShieldType) spells[spell_id].DamageShieldType; switch (spells[spell_id].resisttype) { case RESIST_COLD: return DS_TORMENT; case RESIST_FIRE: return DS_BURN; case RESIST_DISEASE: return DS_DECAY; default: return DS_THORNS; } } else if (DSType) return (DmgShieldType) DSType; return DS_THORNS; } bool IsLDoNObjectSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_AppraiseLDonChest) || IsEffectInSpell(spell_id, SE_DisarmLDoNTrap) || IsEffectInSpell(spell_id, SE_UnlockLDoNChest)) return true; return false; } int32 GetSpellResistType(uint16 spell_id) { return spells[spell_id].resisttype; } int32 GetSpellTargetType(uint16 spell_id) { return (int32)spells[spell_id].targettype; } bool IsHealOverTimeSpell(uint16 spell_id) { if (IsEffectInSpell(spell_id, SE_HealOverTime) && !IsGroupSpell(spell_id)) return true; return false; } bool IsCompleteHealSpell(uint16 spell_id) { if (spell_id == 13 || IsEffectInSpell(spell_id, SE_CompleteHeal) || (IsPercentalHealSpell(spell_id) && !IsGroupSpell(spell_id))) return true; return false; } bool IsFastHealSpell(uint16 spell_id) { const int MaxFastHealCastingTime = 2000; if (spells[spell_id].cast_time <= MaxFastHealCastingTime && spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && !IsGroupSpell(spell_id)) return true; return false; } bool IsVeryFastHealSpell(uint16 spell_id) { const int MaxFastHealCastingTime = 1000; if (spells[spell_id].cast_time <= MaxFastHealCastingTime && spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && !IsGroupSpell(spell_id)) return true; return false; } bool IsRegularSingleTargetHealSpell(uint16 spell_id) { if(spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && spells[spell_id].targettype == ST_Target && spells[spell_id].buffduration == 0 && !IsFastHealSpell(spell_id) && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id)) return true; return false; } bool IsRegularGroupHealSpell(uint16 spell_id) { if (IsGroupSpell(spell_id) && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id)) return true; return false; } bool IsGroupCompleteHealSpell(uint16 spell_id) { if (IsGroupSpell(spell_id) && IsCompleteHealSpell(spell_id)) return true; return false; } bool IsGroupHealOverTimeSpell(uint16 spell_id) { if( IsGroupSpell(spell_id) && IsHealOverTimeSpell(spell_id) && spells[spell_id].buffduration < 10) return true; return false; } bool IsDebuffSpell(uint16 spell_id) { if (IsBeneficialSpell(spell_id) || IsEffectHitpointsSpell(spell_id) || IsStunSpell(spell_id) || IsMezSpell(spell_id) || IsCharmSpell(spell_id) || IsSlowSpell(spell_id) || IsEffectInSpell(spell_id, SE_Root) || IsEffectInSpell(spell_id, SE_CancelMagic) || IsEffectInSpell(spell_id, SE_MovementSpeed) || IsFearSpell(spell_id) || IsEffectInSpell(spell_id, SE_InstantHate)) return false; else return true; } bool IsResistDebuffSpell(uint16 spell_id) { if ((IsEffectInSpell(spell_id, SE_ResistFire) || IsEffectInSpell(spell_id, SE_ResistCold) || IsEffectInSpell(spell_id, SE_ResistPoison) || IsEffectInSpell(spell_id, SE_ResistDisease) || IsEffectInSpell(spell_id, SE_ResistMagic) || IsEffectInSpell(spell_id, SE_ResistAll) || IsEffectInSpell(spell_id, SE_ResistCorruption)) && !IsBeneficialSpell(spell_id)) return true; else return false; } bool IsSelfConversionSpell(uint16 spell_id) { if (GetSpellTargetType(spell_id) == ST_Self && IsEffectInSpell(spell_id, SE_CurrentMana) && IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 && spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0) return true; else return false; } // returns true for both detrimental and beneficial buffs bool IsBuffSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && (spells[spell_id].buffduration || spells[spell_id].buffdurationformula)) return true; return false; } bool IsPersistDeathSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && spells[spell_id].persistdeath) return true; return false; } bool IsSuspendableSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && spells[spell_id].suspendable) return true; return false; } uint32 GetMorphTrigger(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect) return spells[spell_id].base[i]; return 0; } bool IsCastonFadeDurationSpell(uint16 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) { if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect || spells[spell_id].effectid[i] == SE_CastOnFadeEffectNPC || spells[spell_id].effectid[i] == SE_CastOnFadeEffectAlways){ return true; } } return false; } bool IsPowerDistModSpell(uint16 spell_id) { if (IsValidSpell(spell_id) && (spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist) return true; return false; } uint32 GetPartialMeleeRuneReduction(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) if (spells[spell_id].effectid[i] == SE_MitigateMeleeDamage) return spells[spell_id].base[i]; return 0; } uint32 GetPartialMagicRuneReduction(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) if (spells[spell_id].effectid[i] == SE_MitigateSpellDamage) return spells[spell_id].base[i]; return 0; } uint32 GetPartialMeleeRuneAmount(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) if (spells[spell_id].effectid[i] == SE_MitigateMeleeDamage) return spells[spell_id].max[i]; return 0; } uint32 GetPartialMagicRuneAmount(uint32 spell_id) { for (int i = 0; i < EFFECT_COUNT; ++i) if (spells[spell_id].effectid[i] == SE_MitigateSpellDamage) return spells[spell_id].max[i]; return 0; } bool DetrimentalSpellAllowsRest(uint16 spell_id) { if (IsValidSpell(spell_id)) return spells[spell_id].AllowRest; return false; } uint32 GetNimbusEffect(uint16 spell_id) { if (IsValidSpell(spell_id)) return (int32)spells[spell_id].NimbusEffect; return 0; } int32 GetFuriousBash(uint16 spell_id) { if (!IsValidSpell(spell_id)) return 0; bool found_effect_limit = false; int32 mod = 0; for (int i = 0; i < EFFECT_COUNT; ++i) if (spells[spell_id].effectid[i] == SE_SpellHateMod) mod = spells[spell_id].base[i]; else if (spells[spell_id].effectid[i] == SE_LimitEffect && spells[spell_id].base[i] == 999) found_effect_limit = true; if (found_effect_limit) return mod; else return 0; } bool IsShortDurationBuff(uint16 spell_id) { if (IsValidSpell(spell_id) && spells[spell_id].short_buff_box != 0) return true; return false; } bool IsSpellUsableThisZoneType(uint16 spell_id, uint8 zone_type) { //check if spell can be cast in any zone (-1 or 255), then if spell zonetype matches zone's zonetype // || spells[spell_id].zonetype == 255 comparing signed 8 bit int to 255 is always false if (IsValidSpell(spell_id) && (spells[spell_id].zonetype == -1 || spells[spell_id].zonetype == zone_type)) return true; return false; } const char* GetSpellName(int16 spell_id) { return spells[spell_id].name; }