diff options
Diffstat (limited to 'src')
22 files changed, 447 insertions, 329 deletions
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index f43a7f91f32..c5b5cd5dfef 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -433,11 +433,12 @@ void SmartAI::MovementInform(uint32 MovementType, uint32 Data) void SmartAI::RemoveAuras() { + /// @fixme: duplicated logic in CreatureAI::_EnterEvadeMode (could use RemoveAllAurasExceptType) Unit::AuraApplicationMap& appliedAuras = me->GetAppliedAuras(); for (Unit::AuraApplicationMap::iterator iter = appliedAuras.begin(); iter != appliedAuras.end();) { Aura const* aura = iter->second->GetBase(); - if (!aura->IsPassive() && !aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE) && aura->GetCasterGUID() != me->GetGUID()) + if (!aura->IsPassive() && !aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE) && !aura->HasEffectType(SPELL_AURA_CLONE_CASTER) && aura->GetCasterGUID() != me->GetGUID()) me->RemoveAura(iter); else ++iter; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 65b1e6c331e..b4e18b66a69 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -1401,7 +1401,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u e.GetTargetType() == SMART_TARGET_GAMEOBJECT_GUID || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_DISTANCE || e.GetTargetType() == SMART_TARGET_CLOSEST_CREATURE || e.GetTargetType() == SMART_TARGET_CLOSEST_GAMEOBJECT || e.GetTargetType() == SMART_TARGET_OWNER_OR_SUMMONER || e.GetTargetType() == SMART_TARGET_ACTION_INVOKER || - e.GetTargetType() == SMART_TARGET_CLOSEST_ENEMY) + e.GetTargetType() == SMART_TARGET_CLOSEST_ENEMY || e.GetTargetType() == SMART_TARGET_CLOSEST_FRIENDLY) { ObjectList* targets = GetTargets(e, unit); if (!targets) @@ -2506,6 +2506,14 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* break; } + case SMART_TARGET_CLOSEST_FRIENDLY: + { + if (me) + if (Unit* target = DoFindClosestFriendlyInRange(e.target.closestFriendly.maxDist)) + l->push_back(target); + + break; + } case SMART_TARGET_POSITION: default: break; @@ -2623,7 +2631,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui return; Unit* target = DoSelectLowestHpFriendly((float)e.event.friendlyHealt.radius, e.event.friendlyHealt.hpDeficit); - if (!target) + if (!target || !target->IsInCombat()) return; ProcessTimedAction(e, e.event.friendlyHealt.repeatMin, e.event.friendlyHealt.repeatMax, target); break; @@ -2940,6 +2948,55 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui ProcessAction(e, unit, var0); break; } + case SMART_EVENT_FRIENDLY_HEALTH_PCT: + { + if (!me || !me->IsInCombat()) + return; + + ObjectList* _targets = NULL; + + switch (e.GetTargetType()) + { + case SMART_TARGET_CREATURE_RANGE: + case SMART_TARGET_CREATURE_GUID: + case SMART_TARGET_CREATURE_DISTANCE: + case SMART_TARGET_CLOSEST_CREATURE: + case SMART_TARGET_CLOSEST_PLAYER: + case SMART_TARGET_PLAYER_RANGE: + case SMART_TARGET_PLAYER_DISTANCE: + _targets = GetTargets(e); + break; + default: + return; + } + + if (!_targets) + return; + + Unit* target = NULL; + + for (ObjectList::const_iterator itr = _targets->begin(); itr != _targets->end(); ++itr) + { + if (IsUnit(*itr) && me->IsFriendlyTo((*itr)->ToUnit()) && (*itr)->ToUnit()->IsAlive() && (*itr)->ToUnit()->IsInCombat()) + { + uint32 healthPct = uint32((*itr)->ToUnit()->GetHealthPct()); + + if (healthPct > e.event.friendlyHealtPct.maxHpPct || healthPct < e.event.friendlyHealtPct.minHpPct) + continue; + + target = (*itr)->ToUnit(); + break; + } + } + + delete _targets; + + if (!target) + return; + + ProcessTimedAction(e, e.event.friendlyHealtPct.repeatMin, e.event.friendlyHealtPct.repeatMax, target); + break; + } default: TC_LOG_ERROR(LOG_FILTER_SQL, "SmartScript::ProcessEvent: Unhandled Event type %u", e.GetEventType()); break; @@ -3019,6 +3076,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff) case SMART_EVENT_HAS_AURA: case SMART_EVENT_TARGET_BUFFED: case SMART_EVENT_IS_BEHIND_TARGET: + case SMART_EVENT_FRIENDLY_HEALTH_PCT: { ProcessEvent(e); if (e.GetScriptType() == SMART_SCRIPT_TYPE_TIMED_ACTIONLIST) @@ -3324,6 +3382,18 @@ void SmartScript::DoFindFriendlyMissingBuff(std::list<Creature*>& list, float ra cell.Visit(p, grid_creature_searcher, *me->GetMap(), *me, range); } +Unit* SmartScript::DoFindClosestFriendlyInRange(float range) +{ + if (!me) + return NULL; + + Unit* unit = NULL; + Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(me, me, range); + Trinity::UnitLastSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(me, unit, u_check); + me->VisitNearbyObject(range, searcher); + return unit; +} + void SmartScript::SetScript9(SmartScriptHolder& e, uint32 entry) { mTimedActionList.clear(); diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h index 6801c132331..b22f2d81b26 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.h +++ b/src/server/game/AI/SmartScripts/SmartScript.h @@ -89,6 +89,7 @@ class SmartScript Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff); void DoFindFriendlyCC(std::list<Creature*>& _list, float range); void DoFindFriendlyMissingBuff(std::list<Creature*>& list, float range, uint32 spellid); + Unit* DoFindClosestFriendlyInRange(float range); void StoreTargetList(ObjectList* targets, uint32 id) { diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 99693355d70..1e20d2e6fbb 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -301,6 +301,7 @@ bool SmartAIMgr::IsTargetValid(SmartScriptHolder const& e) case SMART_TARGET_CLOSEST_GAMEOBJECT: case SMART_TARGET_CLOSEST_CREATURE: case SMART_TARGET_CLOSEST_ENEMY: + case SMART_TARGET_CLOSEST_FRIENDLY: case SMART_TARGET_STORED: break; default: @@ -544,6 +545,31 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) } break; } + case SMART_EVENT_FRIENDLY_HEALTH_PCT: + if (!IsMinMaxValid(e, e.event.friendlyHealtPct.repeatMin, e.event.friendlyHealtPct.repeatMax)) + return false; + + if (e.event.friendlyHealtPct.maxHpPct > 100 || e.event.friendlyHealtPct.minHpPct > 100) + { + TC_LOG_ERROR(LOG_FILTER_SQL, "SmartAIMgr: Entry %d SourceType %u Event %u Action %u has pct value above 100, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); + return false; + } + + switch (e.GetTargetType()) + { + case SMART_TARGET_CREATURE_RANGE: + case SMART_TARGET_CREATURE_GUID: + case SMART_TARGET_CREATURE_DISTANCE: + case SMART_TARGET_CLOSEST_CREATURE: + case SMART_TARGET_CLOSEST_PLAYER: + case SMART_TARGET_PLAYER_RANGE: + case SMART_TARGET_PLAYER_DISTANCE: + break; + default: + TC_LOG_ERROR(LOG_FILTER_SQL, "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid target_type %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetTargetType()); + return false; + } + break; case SMART_EVENT_GO_STATE_CHANGED: case SMART_EVENT_GO_EVENT_INFORM: case SMART_EVENT_TIMED_EVENT_TRIGGERED: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 740be9276b2..05880c07ff4 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -155,8 +155,9 @@ enum SMART_EVENT SMART_EVENT_GO_EVENT_INFORM = 71, // eventId SMART_EVENT_ACTION_DONE = 72, // eventId (SharedDefines.EventId) SMART_EVENT_ON_SPELLCLICK = 73, // clicker (unit) + SMART_EVENT_FRIENDLY_HEALTH_PCT = 74, // minHpPct, maxHpPct, repeatMin, repeatMax - SMART_EVENT_END = 74 + SMART_EVENT_END = 75 }; struct SmartEvent @@ -363,6 +364,14 @@ struct SmartEvent struct { + uint32 minHpPct; + uint32 maxHpPct; + uint32 repeatMin; + uint32 repeatMax; + } friendlyHealtPct; + + struct + { uint32 param1; uint32 param2; uint32 param3; @@ -1001,7 +1010,9 @@ enum SMARTAI_TARGETS SMART_TARGET_OWNER_OR_SUMMONER = 23, // Unit's owner or summoner SMART_TARGET_THREAT_LIST = 24, // All units on creature's threat list SMART_TARGET_CLOSEST_ENEMY = 25, // maxDist - SMART_TARGET_END = 26 + SMART_TARGET_CLOSEST_FRIENDLY = 26, // maxDist + + SMART_TARGET_END = 27 }; struct SmartTarget @@ -1090,6 +1101,11 @@ struct SmartTarget struct { + uint32 maxDist; + } closestFriendly; + + struct + { uint32 param1; uint32 param2; uint32 param3; @@ -1225,7 +1241,8 @@ const uint32 SmartAIEventMask[SMART_EVENT_END][2] = {SMART_EVENT_GO_STATE_CHANGED, SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, {SMART_EVENT_GO_EVENT_INFORM, SMART_SCRIPT_TYPE_MASK_GAMEOBJECT }, {SMART_EVENT_ACTION_DONE, SMART_SCRIPT_TYPE_MASK_CREATURE }, - {SMART_EVENT_ON_SPELLCLICK, SMART_SCRIPT_TYPE_MASK_CREATURE } + {SMART_EVENT_ON_SPELLCLICK, SMART_SCRIPT_TYPE_MASK_CREATURE }, + {SMART_EVENT_FRIENDLY_HEALTH_PCT, SMART_SCRIPT_TYPE_MASK_CREATURE }, }; enum SmartEventFlags diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp index d13fc6d697b..03c4b84b817 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp @@ -973,8 +973,7 @@ bool BattlegroundSA::CheckAchievementCriteriaMeet(uint32 criteriaId, Player cons case BG_CRITERIA_CHECK_NOT_EVEN_A_SCRATCH: return _allVehiclesAlive[GetTeamIndexByTeamId(source->GetTeam())]; case BG_CRITERIA_CHECK_DEFENSE_OF_THE_ANCIENTS: - if (source->GetTeamId() != Attackers && !gateDestroyed) - return true; + return source->GetTeamId() != Attackers && !gateDestroyed; } return Battleground::CheckAchievementCriteriaMeet(criteriaId, source, target, miscValue); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp index 419a4eff84c..5e0cade9b37 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp @@ -877,9 +877,7 @@ bool BattlegroundWS::CheckAchievementCriteriaMeet(uint32 criteriaId, Player cons switch (criteriaId) { case BG_CRITERIA_CHECK_SAVE_THE_DAY: - if (GetFlagState(player->GetTeam()) == BG_WS_FLAG_STATE_ON_BASE) - return true; - break; + return GetFlagState(player->GetTeam()) == BG_WS_FLAG_STATE_ON_BASE; } return Battleground::CheckAchievementCriteriaMeet(criteriaId, player, target, miscValue); diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 4c8d48222e5..69a381ceb3d 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1548,16 +1548,16 @@ void Pet::InitLevelupSpellsForLevel() { for (uint8 i = 0; i < MAX_CREATURE_SPELL_DATA_SLOT; ++i) { - SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(defSpells->spellid[i]); - if (!spellEntry) + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(defSpells->spellid[i]); + if (!spellInfo) continue; // will called first if level down - if (spellEntry->SpellLevel > level) - unlearnSpell(spellEntry->Id, true); + if (spellInfo->SpellLevel > level) + unlearnSpell(spellInfo->Id, true); // will called if level up else - learnSpell(spellEntry->Id); + learnSpell(spellInfo->Id); } } } diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index 3b283ca39c2..ddea24c6439 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -109,7 +109,7 @@ void Totem::UnSummon(uint32 msTime) RemoveAurasDueToSpell(GetSpell(), GetGUID()); // clear owner's totem slot - for (int i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) + for (uint8 i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) { if (GetOwner()->m_SummonSlot[i] == GetGUID()) { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ef695bb1eea..629b202470d 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -162,7 +162,7 @@ ProcEventInfo::ProcEventInfo(Unit* actor, Unit* actionTarget, Unit* procTarget, #endif Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), m_movedPlayer(NULL), m_lastSanctuaryTime(0), - m_TempSpeed(0.0f), IsAIEnabled(false), NeedChangeAI(false), + IsAIEnabled(false), NeedChangeAI(false), m_ControlledByPlayer(false), movespline(new Movement::MoveSpline()), i_AI(NULL), i_disabledAI(NULL), m_AutoRepeatFirstCast(false), m_procDeep(0), m_removedAurasCount(0), i_motionMaster(this), m_ThreatManager(this), @@ -195,8 +195,6 @@ Unit::Unit(bool isWorldObject) : for (uint8 i = 0; i < CURRENT_MAX_SPELL; ++i) m_currentSpells[i] = NULL; - m_addDmgOnce = 0; - for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i) m_SummonSlot[i] = 0; @@ -2970,11 +2968,11 @@ void Unit::SetCurrentCastedSpell(Spell* pSpell) if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]) { // break autorepeat if not Auto Shot - if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id != 75) + if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 75) InterruptSpell(CURRENT_AUTOREPEAT_SPELL); m_AutoRepeatFirstCast = true; } - if (pSpell->m_spellInfo->CalcCastTime(this) > 0) + if (pSpell->GetCastTime() > 0) AddUnitState(UNIT_STATE_CASTING); break; @@ -2987,7 +2985,7 @@ void Unit::SetCurrentCastedSpell(Spell* pSpell) // it also does break autorepeat if not Auto Shot if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && - m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id != 75) + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->GetSpellInfo()->Id != 75) InterruptSpell(CURRENT_AUTOREPEAT_SPELL); AddUnitState(UNIT_STATE_CASTING); @@ -2996,7 +2994,7 @@ void Unit::SetCurrentCastedSpell(Spell* pSpell) case CURRENT_AUTOREPEAT_SPELL: { // only Auto Shoot does not break anything - if (pSpell->m_spellInfo->Id != 75) + if (pSpell->GetSpellInfo()->Id != 75) { // generic autorepeats break generic non-delayed and channeled non-delayed spells InterruptSpell(CURRENT_GENERIC_SPELL, false); @@ -7588,7 +7586,8 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp // Blood of the North // Reaping // Death Rune Mastery - if (dummySpell->SpellIconID == 3041 || dummySpell->SpellIconID == 22 || dummySpell->SpellIconID == 2622) + /// @todo move those to spell scripts + if (dummySpell->SpellIconID == 3041 || (dummySpell->SpellIconID == 22 && dummySpell->Id != 62459) || dummySpell->SpellIconID == 2622) { *handled = true; // Convert recently used Blood Rune to Death Rune @@ -8143,12 +8142,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg return false; break; } - // Rogue T10 4P bonus, should proc on victim - case 70803: - { - target = victim; - break; - } } break; } @@ -14072,11 +14065,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (spellInfo->AttributesEx3 & SPELL_ATTR3_DISABLE_PROC) SetCantProc(true); - i->aura->CallScriptProcHandlers(aurApp, eventInfo); + bool handled = i->aura->CallScriptProcHandlers(aurApp, eventInfo); - // This bool is needed till separate aura effect procs are still here - bool handled = false; - if (HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled)) + // "handled" is needed as long as proc can be handled in multiple places + if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled)) { TC_LOG_DEBUG(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), Id); takeCharges = true; @@ -14359,22 +14351,6 @@ Player* Unit::GetSpellModOwner() const } ///----------Pet responses methods----------------- -void Unit::SendPetCastFail(uint32 spellid, SpellCastResult result) -{ - if (result == SPELL_CAST_OK) - return; - - Unit* owner = GetCharmerOrOwner(); - if (!owner || owner->GetTypeId() != TYPEID_PLAYER) - return; - - WorldPacket data(SMSG_PET_CAST_FAILED, 1 + 4 + 1); - data << uint8(0); // cast count - data << uint32(spellid); - data << uint8(result); - owner->ToPlayer()->GetSession()->SendPacket(&data); -} - void Unit::SendPetActionFeedback(uint8 msg) { Unit* owner = GetOwner(); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 5e2e746ae63..14a4decb390 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -951,7 +951,8 @@ struct CalcDamageInfo }; // Spell damage info structure based on structure sending in SMSG_SPELLNONMELEEDAMAGELOG opcode -struct SpellNonMeleeDamage{ +struct SpellNonMeleeDamage +{ SpellNonMeleeDamage(Unit* _attacker, Unit* _target, uint32 _SpellID, uint32 _schoolMask) : target(_target), attacker(_attacker), SpellID(_SpellID), damage(0), overkill(0), schoolMask(_schoolMask), absorb(0), resist(0), physicalLog(false), unused(false), blocked(0), HitInfo(0), cleanDamage(0) @@ -1647,8 +1648,8 @@ class Unit : public WorldObject Player* GetSpellModOwner() const; Unit* GetOwner() const; - Guardian *GetGuardianPet() const; - Minion *GetFirstMinion() const; + Guardian* GetGuardianPet() const; + Minion* GetFirstMinion() const; Unit* GetCharmer() const; Unit* GetCharm() const; Unit* GetCharmerOrOwner() const; @@ -1839,7 +1840,6 @@ class Unit : public WorldObject Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; int32 GetCurrentSpellCastTime(uint32 spell_id) const; - uint32 m_addDmgOnce; uint64 m_SummonSlot[MAX_SUMMON_SLOT]; uint64 m_ObjectSlot[MAX_GAMEOBJECT_SLOT]; @@ -1995,9 +1995,6 @@ class Unit : public WorldObject float GetSpeed(UnitMoveType mtype) const; float GetSpeedRate(UnitMoveType mtype) const { return m_speed_rate[mtype]; } void SetSpeed(UnitMoveType mtype, float rate, bool forced = false); - float m_TempSpeed; - - bool isHover() const { return HasAuraType(SPELL_AURA_HOVER); } float ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const; int32 CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effect_index, int32 const* basePoints = NULL) const; @@ -2039,7 +2036,6 @@ class Unit : public WorldObject void ClearComboPointHolders(); ///----------Pet responses methods----------------- - void SendPetCastFail(uint32 spellid, SpellCastResult msg); void SendPetActionFeedback (uint8 msg); void SendPetTalk (uint32 pettalk); void SendPetAIReaction(uint64 guid); diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 84bdaf4104f..32cd7ee4725 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -295,10 +295,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid return; } - if (spellInfo->StartRecoveryCategory > 0) - if (pet->GetCharmInfo() && pet->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) - return; - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_SRC_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_DEST_AREA_ENEMY || spellInfo->Effects[i].TargetA.GetTarget() == TARGET_DEST_DYNOBJ_ENEMY) @@ -378,10 +374,10 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid } else { - if (pet->isPossessed() || pet->IsVehicle()) + if (pet->isPossessed() || pet->IsVehicle()) /// @todo: confirm this check Spell::SendCastResult(GetPlayer(), spellInfo, 0, result); else - pet->SendPetCastFail(spellid, result); + spell->SendPetCastResult(result); if (!pet->ToCreature()->HasSpellCooldown(spellid)) GetPlayer()->SendClearCooldown(spellid, pet); @@ -771,13 +767,6 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) return; } - if (spellInfo->StartRecoveryCategory > 0) // Check if spell is affected by GCD - if (caster->GetTypeId() == TYPEID_UNIT && caster->GetCharmInfo() && caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) - { - caster->SendPetCastFail(spellId, SPELL_FAILED_NOT_READY); - return; - } - // do not cast not learned spells if (!caster->HasSpell(spellId) || spellInfo->IsPassive()) return; @@ -792,25 +781,19 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) spell->m_cast_count = castCount; // probably pending spell cast spell->m_targets = targets; - /// @todo need to check victim? - SpellCastResult result; - if (caster->m_movedPlayer) - result = spell->CheckPetCast(caster->m_movedPlayer->GetSelectedUnit()); - else - result = spell->CheckPetCast(NULL); + SpellCastResult result = spell->CheckPetCast(NULL); + if (result == SPELL_CAST_OK) { - if (caster->GetTypeId() == TYPEID_UNIT) + if (Creature* creature = caster->ToCreature()) { - Creature* pet = caster->ToCreature(); - pet->AddCreatureSpellCooldown(spellId); - if (pet->IsPet()) + creature->AddCreatureSpellCooldown(spellId); + if (Pet* pet = creature->ToPet()) { - Pet* p = (Pet*)pet; // 10% chance to play special pet attack talk, else growl // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell - if (p->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) - pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); + if (pet->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) + pet->SendPetTalk(PET_TALK_SPECIAL_SPELL); else pet->SendPetAIReaction(guid); } @@ -820,7 +803,8 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket) } else { - caster->SendPetCastFail(spellId, result); + spell->SendPetCastResult(result); + if (caster->GetTypeId() == TYPEID_PLAYER) { if (!caster->ToPlayer()->HasSpellCooldown(spellId)) diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 2846137ad95..51a04749c30 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -2269,8 +2269,9 @@ bool Aura::CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEven return prepare; } -void Aura::CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) +bool Aura::CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) { + bool handled = false; for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PROC, aurApp); @@ -2278,8 +2279,11 @@ void Aura::CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& for (; hookItr != hookItrEnd; ++hookItr) hookItr->Call(*scritr, eventInfo); + handled |= (*scritr)->_IsDefaultActionPrevented(); (*scritr)->_FinishScriptCall(); } + + return handled; } void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index e865d415438..9e7d0cce82c 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -229,7 +229,7 @@ class Aura // Spell Proc Hooks bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); - void CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); + bool CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); void CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); bool CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); void CallScriptAfterEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 13a6cffeb12..be88179b168 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2038,6 +2038,15 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar } } +GameObject* Spell::SearchSpellFocus() +{ + GameObject* focus = NULL; + Trinity::GameObjectFocusCheck check(m_caster, m_spellInfo->RequiresSpellFocus); + Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> searcher(m_caster, focus, check); + SearchTargets<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> > (searcher, GRID_MAP_TYPE_MASK_GAMEOBJECT, m_caster, m_caster, m_caster->GetVisibilityRange()); + return focus; +} + void Spell::prepareDataForTriggerSystem(AuraEffect const* /*triggeredByAura*/) { //========================================================================================== @@ -3041,18 +3050,20 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered // Prepare data for triggers prepareDataForTriggerSystem(triggeredByAura); - if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->SetSpellModTakingSpell(this, true); - // calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail) - m_casttime = m_spellInfo->CalcCastTime(m_caster, this); - if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (Player* player = m_caster->ToPlayer()) { - m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); - - // Set casttime to 0 if .cheat casttime is enabled. - if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_CASTTIME)) - m_casttime = 0; + if (!m_caster->ToPlayer()->GetCommandStatus(CHEAT_CASTTIME)) + { + m_caster->ToPlayer()->SetSpellModTakingSpell(this, true); + // calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail) + m_casttime = m_spellInfo->CalcCastTime(this); + m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); + } + else + m_casttime = 0; // Set cast time to 0 if .cheat casttime is enabled. } + else + m_casttime = m_spellInfo->CalcCastTime(this); // don't allow channeled spells / spells with cast time to be casted while moving // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) @@ -3314,7 +3325,7 @@ void Spell::cast(bool skipCheck) SendSpellGo(); // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells - if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled()) || m_spellInfo->Id == 14157) + if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled()) || m_spellInfo->AttributesEx4 & SPELL_ATTR4_UNK4) { // Remove used for cast item if need (it can be already NULL after TakeReagents call // in case delayed spell remove item at cast delay start @@ -3755,27 +3766,9 @@ void Spell::finish(bool ok) m_caster->AttackStop(); } -void Spell::SendCastResult(SpellCastResult result) -{ - if (result == SPELL_CAST_OK) - return; - - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (m_caster->ToPlayer()->GetSession()->PlayerLoading()) // don't send cast results at loading time - return; - - SendCastResult(m_caster->ToPlayer(), m_spellInfo, m_cast_count, result, m_customError); -} - -void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cast_count, SpellCastResult result, SpellCustomErrors customError /*= SPELL_CUSTOM_ERROR_NONE*/) +void Spell::WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError) { - if (result == SPELL_CAST_OK) - return; - - WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); - data << uint8(cast_count); // single cast or multi 2.3 (0/1) + data << uint8(castCount); // single cast or multi 2.3 (0/1) data << uint32(spellInfo->Id); data << uint8(result); // problem switch (result) @@ -3876,9 +3869,52 @@ void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cas default: break; } +} + +void Spell::SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError /*= SPELL_CUSTOM_ERROR_NONE*/) +{ + if (result == SPELL_CAST_OK) + return; + + WorldPacket data(SMSG_CAST_FAILED, 1 + 4 + 1); + WriteCastResultInfo(data, caster, spellInfo, castCount, result, customError); + caster->GetSession()->SendPacket(&data); } +void Spell::SendCastResult(SpellCastResult result) +{ + if (result == SPELL_CAST_OK) + return; + + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return; + + if (m_caster->ToPlayer()->GetSession()->PlayerLoading()) // don't send cast results at loading time + return; + + SendCastResult(m_caster->ToPlayer(), m_spellInfo, m_cast_count, result, m_customError); +} + +void Spell::SendPetCastResult(SpellCastResult result) +{ + if (result == SPELL_CAST_OK) + return; + + Unit* owner = m_caster->GetCharmerOrOwner(); + if (!owner) + return; + + Player* player = owner->ToPlayer(); + if (!player) + return; + + WorldPacket data(SMSG_PET_CAST_FAILED, 1 + 4 + 1); + WriteCastResultInfo(data, player, m_spellInfo, m_cast_count, result, m_customError); + + player->GetSession()->SendPacket(&data); +} + void Spell::SendSpellStart() { if (!IsNeedSendToClient()) @@ -4981,9 +5017,17 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_NOT_MOUNTED; } + // check spell focus object + if (m_spellInfo->RequiresSpellFocus) + { + focusObject = SearchSpellFocus(); + if (!focusObject) + return SPELL_FAILED_REQUIRES_SPELL_FOCUS; + } + SpellCastResult castResult = SPELL_CAST_OK; - // always (except passive spells) check items (focus object can be required for any type casts) + // always (except passive spells) check items (only player related checks) if (!m_spellInfo->IsPassive()) { castResult = CheckItems(); @@ -5585,6 +5629,11 @@ SpellCastResult Spell::CheckPetCast(Unit* target) if (creatureCaster->HasSpellCooldown(m_spellInfo->Id)) return SPELL_FAILED_NOT_READY; + // Check if spell is affected by GCD + if (m_spellInfo->StartRecoveryCategory > 0) + if (m_caster->GetCharmInfo() && m_caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo)) + return SPELL_FAILED_NOT_READY; + return CheckCast(true); } @@ -5947,26 +5996,6 @@ SpellCastResult Spell::CheckItems() return SPELL_FAILED_EQUIPPED_ITEM_CLASS; } - // check spell focus object - if (m_spellInfo->RequiresSpellFocus) - { - CellCoord p(Trinity::ComputeCellCoord(m_caster->GetPositionX(), m_caster->GetPositionY())); - Cell cell(p); - - GameObject* ok = NULL; - Trinity::GameObjectFocusCheck go_check(m_caster, m_spellInfo->RequiresSpellFocus); - Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck> checker(m_caster, ok, go_check); - - TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck>, GridTypeMapContainer > object_checker(checker); - Map& map = *m_caster->GetMap(); - cell.Visit(p, object_checker, map, *m_caster, m_caster->GetVisibilityRange()); - - if (!ok) - return SPELL_FAILED_REQUIRES_SPELL_FOCUS; - - focusObject = ok; // game object found in range - } - // do not take reagents for these item casts if (!(m_CastItem && m_CastItem->GetTemplate()->Flags & ITEM_PROTO_FLAG_TRIGGERED_CAST)) { diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 191a7461fe9..927ecd32f29 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -370,6 +370,8 @@ class Spell void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList); void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, ConditionList* condList, bool isChainHeal); + GameObject* SearchSpellFocus(); + void prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura = NULL); void cancel(); void update(uint32 difftime); @@ -415,8 +417,10 @@ class Spell void CheckSrc() { if (!m_targets.HasSrc()) m_targets.SetSrc(*m_caster); } void CheckDst() { if (!m_targets.HasDst()) m_targets.SetDst(*m_caster); } - static void SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 cast_count, SpellCastResult result, SpellCustomErrors customError = SPELL_CUSTOM_ERROR_NONE); + static void WriteCastResultInfo(WorldPacket& data, Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError); + static void SendCastResult(Player* caster, SpellInfo const* spellInfo, uint8 castCount, SpellCastResult result, SpellCustomErrors customError = SPELL_CUSTOM_ERROR_NONE); void SendCastResult(SpellCastResult result); + void SendPetCastResult(SpellCastResult result); void SendSpellStart(); void SendSpellGo(); void SendSpellCooldown(); diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index a58c2ee19e4..04437b82f2d 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1006,8 +1006,12 @@ bool SpellInfo::NeedsExplicitUnitTarget() const bool SpellInfo::NeedsToBeTriggeredByCaster() const { + if (AttributesCu & SPELL_ATTR0_CU_TRIGGERED_BY_CASTER) + return true; + if (NeedsExplicitUnitTarget()) return true; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (Effects[i].IsEffect()) @@ -1017,6 +1021,7 @@ bool SpellInfo::NeedsToBeTriggeredByCaster() const return true; } } + return false; } @@ -1714,7 +1719,7 @@ uint32 SpellInfo::GetAllEffectsMechanicMask() const uint32 mask = 0; if (Mechanic) mask |= 1 << Mechanic; - for (int i = 0; i < MAX_SPELL_EFFECTS; ++i) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (Effects[i].IsEffect() && Effects[i].Mechanic) mask |= 1 << Effects[i].Mechanic; return mask; @@ -1724,9 +1729,9 @@ uint32 SpellInfo::GetEffectMechanicMask(uint8 effIndex) const { uint32 mask = 0; if (Mechanic) - mask |= 1<< Mechanic; + mask |= 1 << Mechanic; if (Effects[effIndex].IsEffect() && Effects[effIndex].Mechanic) - mask |= 1<< Effects[effIndex].Mechanic; + mask |= 1 << Effects[effIndex].Mechanic; return mask; } @@ -1734,10 +1739,10 @@ uint32 SpellInfo::GetSpellMechanicMaskByEffectMask(uint32 effectMask) const { uint32 mask = 0; if (Mechanic) - mask |= 1<< Mechanic; - for (int i = 0; i < MAX_SPELL_EFFECTS; ++i) + mask |= 1 << Mechanic; + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if ((effectMask & (1 << i)) && Effects[i].Mechanic) - mask |= 1<< Effects[i].Mechanic; + mask |= 1 << Effects[i].Mechanic; return mask; } @@ -2052,7 +2057,7 @@ int32 SpellInfo::GetMaxDuration() const return (DurationEntry->Duration[2] == -1) ? -1 : abs(DurationEntry->Duration[2]); } -uint32 SpellInfo::CalcCastTime(Unit* caster, Spell* spell) const +uint32 SpellInfo::CalcCastTime(Spell* spell /*= NULL*/) const { // not all spells have cast time index and this is all is pasiive abilities if (!CastTimeEntry) @@ -2060,8 +2065,8 @@ uint32 SpellInfo::CalcCastTime(Unit* caster, Spell* spell) const int32 castTime = CastTimeEntry->CastTime; - if (caster) - caster->ModSpellCastTime(this, castTime, spell); + if (spell) + spell->GetCaster()->ModSpellCastTime(this, castTime, spell); if (Attributes & SPELL_ATTR0_REQ_AMMO && (!IsAutoRepeatRangedSpell())) castTime += 500; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 6ed742a3b8e..6acde5afa74 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -176,7 +176,7 @@ enum SpellCustomAttributes SPELL_ATTR0_CU_CONE_LINE = 0x00000004, SPELL_ATTR0_CU_SHARE_DAMAGE = 0x00000008, SPELL_ATTR0_CU_NO_INITIAL_THREAT = 0x00000010, - SPELL_ATTR0_CU_NONE2 = 0x00000020, // UNUSED + SPELL_ATTR0_CU_TRIGGERED_BY_CASTER = 0x00000020, // @todo: need generic solution, some triggered spells will be casted by target instead of caster SPELL_ATTR0_CU_AURA_CC = 0x00000040, SPELL_ATTR0_CU_DIRECT_DAMAGE = 0x00000100, SPELL_ATTR0_CU_CHARGE = 0x00000200, @@ -444,7 +444,7 @@ public: uint32 GetMaxTicks() const; - uint32 CalcCastTime(Unit* caster = NULL, Spell* spell = NULL) const; + uint32 CalcCastTime(Spell* spell = NULL) const; uint32 GetRecoveryTime() const; int32 CalcPowerCost(Unit const* caster, SpellSchoolMask schoolMask) const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 24bd19d2d24..94454e8440d 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -2972,6 +2972,9 @@ void SpellMgr::LoadSpellInfoCustomAttributes() case 72293: // Mark of the Fallen Champion (Deathbringer Saurfang) spellInfo->AttributesCu |= SPELL_ATTR0_CU_NEGATIVE_EFF0; break; + case 38729: // Rod of Purification + spellInfo->AttributesCu |= SPELL_ATTR0_CU_TRIGGERED_BY_CASTER; + break; default: break; } @@ -3103,6 +3106,17 @@ void SpellMgr::LoadSpellInfoCorrections() // Entries were not updated after spell effect change, we have to do that manually :/ spellInfo->AttributesEx3 |= SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED; break; + case 5308: // Execute (Rank 1) + case 20658: // Execute (Rank 2) + case 20660: // Execute (Rank 3) + case 20661: // Execute (Rank 4) + case 20662: // Execute (Rank 5) + case 25234: // Execute (Rank 6) + case 25236: // Execute (Rank 7) + case 47470: // Execute (Rank 8) + case 47471: // Execute (Rank 9) + spellInfo->AttributesEx3 |= SPELL_ATTR3_CANT_TRIGGER_PROC; + break; case 59725: // Improved Spell Reflection - aoe aura // Target entry seems to be wrong for this spell :/ spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER_AREA_PARTY); diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index 5fb4d69cd02..bb9aab023af 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -909,6 +909,7 @@ bool AuraScript::_IsDefaultActionPrevented() case AURA_SCRIPT_HOOK_EFFECT_ABSORB: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: case AURA_SCRIPT_HOOK_PREPARE_PROC: + case AURA_SCRIPT_HOOK_PROC: case AURA_SCRIPT_HOOK_EFFECT_PROC: return m_defaultActionPrevented; default: @@ -927,6 +928,7 @@ void AuraScript::PreventDefaultAction() case AURA_SCRIPT_HOOK_EFFECT_ABSORB: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: case AURA_SCRIPT_HOOK_PREPARE_PROC: + case AURA_SCRIPT_HOOK_PROC: case AURA_SCRIPT_HOOK_EFFECT_PROC: m_defaultActionPrevented = true; break; diff --git a/src/server/scripts/Northrend/DraktharonKeep/boss_tharon_ja.cpp b/src/server/scripts/Northrend/DraktharonKeep/boss_tharon_ja.cpp index e090f3db7c0..efca060b67c 100644 --- a/src/server/scripts/Northrend/DraktharonKeep/boss_tharon_ja.cpp +++ b/src/server/scripts/Northrend/DraktharonKeep/boss_tharon_ja.cpp @@ -17,29 +17,48 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" -#include "drak_tharon_keep.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" #include "Player.h" +#include "drak_tharon_keep.h" + +/* + * Known Issues: Spell 49356 and 53463 will be interrupted for an unknown reason + */ enum Spells { - //skeletal spells (phase 1) + // Skeletal Spells (phase 1) SPELL_CURSE_OF_LIFE = 49527, - H_SPELL_CURSE_OF_LIFE = 59972, SPELL_RAIN_OF_FIRE = 49518, - H_SPELL_RAIN_OF_FIRE = 59971, SPELL_SHADOW_VOLLEY = 49528, - H_SPELL_SHADOW_VOLLEY = 59973, - SPELL_DECAY_FLESH = 49356, //casted at end of phase 1, starts phase 2 - //flesh spells (phase 2) + SPELL_DECAY_FLESH = 49356, // casted at end of phase 1, starts phase 2 + // Flesh Spells (phase 2) SPELL_GIFT_OF_THARON_JA = 52509, + SPELL_CLEAR_GIFT_OF_THARON_JA = 53242, SPELL_EYE_BEAM = 49544, - H_SPELL_EYE_BEAM = 59965, SPELL_LIGHTNING_BREATH = 49537, - H_SPELL_LIGHTNING_BREATH = 59963, SPELL_POISON_CLOUD = 49548, - H_SPELL_POISON_CLOUD = 59969, - SPELL_RETURN_FLESH = 53463, //Channeled spell ending phase two and returning to phase 1. This ability will stun the party for 6 seconds. + SPELL_RETURN_FLESH = 53463, // Channeled spell ending phase two and returning to phase 1. This ability will stun the party for 6 seconds. SPELL_ACHIEVEMENT_CHECK = 61863, + SPELL_FLESH_VISUAL = 52582, + SPELL_DUMMY = 49551 +}; + +enum Events +{ + EVENT_CURSE_OF_LIFE = 1, + EVENT_RAIN_OF_FIRE, + EVENT_SHADOW_VOLLEY, + + EVENT_EYE_BEAM, + EVENT_LIGHTNING_BREATH, + EVENT_POISON_CLOUD, + + EVENT_DECAY_FLESH, + EVENT_GOING_FLESH, + EVENT_RETURN_FLESH, + EVENT_GOING_SKELETAL }; enum Yells @@ -50,204 +69,178 @@ enum Yells SAY_SKELETON = 3, SAY_DEATH = 4 }; + enum Models { - MODEL_FLESH = 27073, - MODEL_SKELETON = 27511 -}; -enum CombatPhase -{ - SKELETAL, - GOING_FLESH, - FLESH, - GOING_SKELETAL + MODEL_FLESH = 27073 }; class boss_tharon_ja : public CreatureScript { -public: - boss_tharon_ja() : CreatureScript("boss_tharon_ja") { } + public: + boss_tharon_ja() : CreatureScript("boss_tharon_ja") { } - struct boss_tharon_jaAI : public ScriptedAI - { - boss_tharon_jaAI(Creature* creature) : ScriptedAI(creature) + struct boss_tharon_jaAI : public BossAI { - instance = creature->GetInstanceScript(); - } - - uint32 uiPhaseTimer; - uint32 uiCurseOfLifeTimer; - uint32 uiRainOfFireTimer; - uint32 uiShadowVolleyTimer; - uint32 uiEyeBeamTimer; - uint32 uiLightningBreathTimer; - uint32 uiPoisonCloudTimer; + boss_tharon_jaAI(Creature* creature) : BossAI(creature, DATA_THARON_JA) { } - CombatPhase Phase; - - InstanceScript* instance; - - void Reset() OVERRIDE - { - uiPhaseTimer = 20*IN_MILLISECONDS; - uiCurseOfLifeTimer = 1*IN_MILLISECONDS; - uiRainOfFireTimer = urand(14*IN_MILLISECONDS, 18*IN_MILLISECONDS); - uiShadowVolleyTimer = urand(8*IN_MILLISECONDS, 10*IN_MILLISECONDS); - Phase = SKELETAL; - me->SetDisplayId(me->GetNativeDisplayId()); - instance->SetBossState(DATA_THARON_JA, NOT_STARTED); - } + void Reset() OVERRIDE + { + _Reset(); + me->RestoreDisplayId(); + } - void EnterCombat(Unit* /*who*/) OVERRIDE - { - Talk(SAY_AGGRO); - instance->SetBossState(DATA_THARON_JA, IN_PROGRESS); - } + void EnterCombat(Unit* /*who*/) OVERRIDE + { + Talk(SAY_AGGRO); + _EnterCombat(); - void UpdateAI(uint32 diff) OVERRIDE - { - //Return since we have no target - if (!UpdateVictim()) - return; + events.ScheduleEvent(EVENT_DECAY_FLESH, 20000); + events.ScheduleEvent(EVENT_CURSE_OF_LIFE, 1000); + events.ScheduleEvent(EVENT_RAIN_OF_FIRE, urand(14000, 18000)); + events.ScheduleEvent(EVENT_SHADOW_VOLLEY, urand(8000, 10000)); + } - switch (Phase) + void KilledUnit(Unit* who) OVERRIDE { - case SKELETAL: - if (uiCurseOfLifeTimer < diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_CURSE_OF_LIFE); - uiCurseOfLifeTimer = urand(10*IN_MILLISECONDS, 15*IN_MILLISECONDS); - } else uiCurseOfLifeTimer -= diff; + if (who->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + } - if (uiShadowVolleyTimer < diff) - { - DoCastVictim(SPELL_SHADOW_VOLLEY); - uiShadowVolleyTimer = urand(8*IN_MILLISECONDS, 10*IN_MILLISECONDS); - } else uiShadowVolleyTimer -= diff; + void JustDied(Unit* /*killer*/) OVERRIDE + { + _JustDied(); - if (uiRainOfFireTimer < diff) - { - DoCastAOE(SPELL_RAIN_OF_FIRE); - uiRainOfFireTimer = urand(14*IN_MILLISECONDS, 18*IN_MILLISECONDS); - } else uiRainOfFireTimer -= diff; + Talk(SAY_DEATH); + DoCastAOE(SPELL_CLEAR_GIFT_OF_THARON_JA, true); + DoCastAOE(SPELL_ACHIEVEMENT_CHECK, true); + } - if (uiPhaseTimer < diff) - { - DoCast(SPELL_DECAY_FLESH); - Phase = GOING_FLESH; - uiPhaseTimer = 6*IN_MILLISECONDS; - } else uiPhaseTimer -= diff; - - DoMeleeAttackIfReady(); - break; - case GOING_FLESH: - if (uiPhaseTimer < diff) - { - Talk(SAY_FLESH); - me->SetDisplayId(MODEL_FLESH); - - std::list<Unit*> playerList; - SelectTargetList(playerList, 5, SELECT_TARGET_TOPAGGRO, 0, true); - for (std::list<Unit*>::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) - { - Unit* temp = (*itr); - me->AddAura(SPELL_GIFT_OF_THARON_JA, temp); - temp->SetDisplayId(MODEL_SKELETON); - } - uiPhaseTimer = 20*IN_MILLISECONDS; - uiLightningBreathTimer = urand(3*IN_MILLISECONDS, 4*IN_MILLISECONDS); - uiEyeBeamTimer = urand(4*IN_MILLISECONDS, 8*IN_MILLISECONDS); - uiPoisonCloudTimer = urand(6*IN_MILLISECONDS, 7*IN_MILLISECONDS); - Phase = FLESH; - } else uiPhaseTimer -= diff; - break; - case FLESH: - if (uiLightningBreathTimer < diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_LIGHTNING_BREATH); - uiLightningBreathTimer = urand(6*IN_MILLISECONDS, 7*IN_MILLISECONDS); - } else uiLightningBreathTimer -= diff; + void UpdateAI(uint32 diff) OVERRIDE + { + if (!UpdateVictim()) + return; - if (uiEyeBeamTimer < diff) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(target, SPELL_EYE_BEAM); - uiEyeBeamTimer = urand(4*IN_MILLISECONDS, 6*IN_MILLISECONDS); - } else uiEyeBeamTimer -= diff; + events.Update(diff); - if (uiPoisonCloudTimer < diff) - { - DoCastAOE(SPELL_POISON_CLOUD); - uiPoisonCloudTimer = urand(10*IN_MILLISECONDS, 12*IN_MILLISECONDS); - } else uiPoisonCloudTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (uiPhaseTimer < diff) - { - DoCast(SPELL_RETURN_FLESH); - Phase = GOING_SKELETAL; - uiPhaseTimer = 6*IN_MILLISECONDS; - } else uiPhaseTimer -= diff; - DoMeleeAttackIfReady(); - break; - case GOING_SKELETAL: - if (uiPhaseTimer < diff) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - Talk(SAY_SKELETON); - me->DeMorph(); - Phase = SKELETAL; - uiPhaseTimer = 20*IN_MILLISECONDS; - uiCurseOfLifeTimer = 1*IN_MILLISECONDS; - uiRainOfFireTimer = urand(14*IN_MILLISECONDS, 18*IN_MILLISECONDS); - uiShadowVolleyTimer = urand(8*IN_MILLISECONDS, 10*IN_MILLISECONDS); - - std::list<Unit*> playerList; - SelectTargetList(playerList, 5, SELECT_TARGET_TOPAGGRO, 0, true); - for (std::list<Unit*>::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) - { - Unit* temp = (*itr); - if (temp->HasAura(SPELL_GIFT_OF_THARON_JA)) - temp->RemoveAura(SPELL_GIFT_OF_THARON_JA); - temp->DeMorph(); - } - } else uiPhaseTimer -= diff; - break; + case EVENT_CURSE_OF_LIFE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_CURSE_OF_LIFE); + events.ScheduleEvent(EVENT_CURSE_OF_LIFE, urand(10000, 15000)); + return; + case EVENT_SHADOW_VOLLEY: + DoCastVictim(SPELL_SHADOW_VOLLEY); + events.ScheduleEvent(EVENT_SHADOW_VOLLEY, urand(8000, 10000)); + return; + case EVENT_RAIN_OF_FIRE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_RAIN_OF_FIRE); + events.ScheduleEvent(EVENT_RAIN_OF_FIRE, urand(14000, 18000)); + return; + case EVENT_LIGHTNING_BREATH: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_LIGHTNING_BREATH); + events.ScheduleEvent(EVENT_LIGHTNING_BREATH, urand(6000, 7000)); + return; + case EVENT_EYE_BEAM: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + DoCast(target, SPELL_EYE_BEAM); + events.ScheduleEvent(EVENT_EYE_BEAM, urand(4000, 6000)); + return; + case EVENT_POISON_CLOUD: + DoCastAOE(SPELL_POISON_CLOUD); + events.ScheduleEvent(EVENT_POISON_CLOUD, urand(10000, 12000)); + return; + case EVENT_DECAY_FLESH: + DoCastAOE(SPELL_DECAY_FLESH); + events.ScheduleEvent(EVENT_GOING_FLESH, 6000); + return; + case EVENT_GOING_FLESH: + Talk(SAY_FLESH); + me->SetDisplayId(MODEL_FLESH); + DoCastAOE(SPELL_GIFT_OF_THARON_JA, true); + DoCast(me, SPELL_FLESH_VISUAL, true); + DoCast(me, SPELL_DUMMY, true); + + events.Reset(); + events.ScheduleEvent(EVENT_RETURN_FLESH, 20000); + events.ScheduleEvent(EVENT_LIGHTNING_BREATH, urand(3000, 4000)); + events.ScheduleEvent(EVENT_EYE_BEAM, urand(4000, 8000)); + events.ScheduleEvent(EVENT_POISON_CLOUD, urand(6000, 7000)); + break; + case EVENT_RETURN_FLESH: + DoCastAOE(SPELL_RETURN_FLESH); + events.ScheduleEvent(EVENT_GOING_SKELETAL, 6000); + return; + case EVENT_GOING_SKELETAL: + Talk(SAY_SKELETON); + me->RestoreDisplayId(); + DoCastAOE(SPELL_CLEAR_GIFT_OF_THARON_JA, true); + + events.Reset(); + events.ScheduleEvent(EVENT_DECAY_FLESH, 20000); + events.ScheduleEvent(EVENT_CURSE_OF_LIFE, 1000); + events.ScheduleEvent(EVENT_RAIN_OF_FIRE, urand(14000, 18000)); + events.ScheduleEvent(EVENT_SHADOW_VOLLEY, urand(8000, 10000)); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); } - } + }; - void KilledUnit(Unit* /*victim*/) OVERRIDE + CreatureAI* GetAI(Creature* creature) const OVERRIDE { - Talk(SAY_KILL); + return GetDrakTharonKeepAI<boss_tharon_jaAI>(creature); } +}; + +class spell_tharon_ja_clear_gift_of_tharon_ja : public SpellScriptLoader +{ + public: + spell_tharon_ja_clear_gift_of_tharon_ja() : SpellScriptLoader("spell_tharon_ja_clear_gift_of_tharon_ja") { } - void JustDied(Unit* /*killer*/) OVERRIDE + class spell_tharon_ja_clear_gift_of_tharon_ja_SpellScript : public SpellScript { - Talk(SAY_DEATH); + PrepareSpellScript(spell_tharon_ja_clear_gift_of_tharon_ja_SpellScript); - if (instance) + bool Validate(SpellInfo const* /*spellInfo*/) OVERRIDE { - // clean morph on players - Map::PlayerList const &PlayerList = instance->instance->GetPlayers(); - - for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) - if (Player* player = i->GetSource()) - player->DeMorph(); + if (!sSpellMgr->GetSpellInfo(SPELL_GIFT_OF_THARON_JA)) + return false; + return true; + } - DoCast(me, SPELL_ACHIEVEMENT_CHECK); + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + target->RemoveAura(SPELL_GIFT_OF_THARON_JA); + } - instance->SetBossState(DATA_THARON_JA, DONE); + void Register() OVERRIDE + { + OnEffectHitTarget += SpellEffectFn(spell_tharon_ja_clear_gift_of_tharon_ja_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } - } - }; + }; - CreatureAI* GetAI(Creature* creature) const OVERRIDE - { - return GetDrakTharonKeepAI<boss_tharon_jaAI>(creature); - } + SpellScript* GetSpellScript() const OVERRIDE + { + return new spell_tharon_ja_clear_gift_of_tharon_ja_SpellScript(); + } }; void AddSC_boss_tharon_ja() { - new boss_tharon_ja; + new boss_tharon_ja(); + new spell_tharon_ja_clear_gift_of_tharon_ja(); } diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index 78dcaaa669c..d022eef65b9 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -578,9 +578,8 @@ class spell_warr_slam : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { - int32 bp0 = GetEffectValue(); if (GetHitUnit()) - GetCaster()->CastCustomSpell(GetHitUnit(), SPELL_WARRIOR_SLAM, &bp0, NULL, NULL, true, 0); + GetCaster()->CastCustomSpell(SPELL_WARRIOR_SLAM, SPELLVALUE_BASE_POINT0, GetEffectValue(), GetHitUnit(), TRIGGERED_FULL_MASK); } void Register() OVERRIDE |
