/* * Copyright (C) 2008-2012 TrinityCore * * 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; either version 2 of the License, or (at your * option) any later version. * * This program 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 this program. If not, see . */ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "SpellScript.h" #include "SpellAuraEffects.h" #include "utgarde_pinnacle.h" enum Spells { SPELL_SVALA_TRANSFORMING1 = 54140, SPELL_SVALA_TRANSFORMING2 = 54205, SPELL_TRANSFORMING_CHANNEL = 54142, SPELL_CALL_FLAMES = 48258, // caster effect only, triggers event 17841 SPELL_SINSTER_STRIKE = 15667, H_SPELL_SINSTER_STRIKE = 59409, SPELL_RITUAL_PREPARATION = 48267, SPELL_RITUAL_OF_THE_SWORD = 48276, SPELL_RITUAL_STRIKE_TRIGGER = 48331, // triggers 48277 & 59930, needs NPC_RITUAL_TARGET as spell_script_target SPELL_RITUAL_DISARM = 54159, SPELL_RITUAL_STRIKE_EFF_1 = 48277, SPELL_RITUAL_STRIKE_EFF_2 = 59930, SPELL_SUMMONED_VIS = 64446, SPELL_RITUAL_CHANNELER_1 = 48271, SPELL_RITUAL_CHANNELER_2 = 48274, SPELL_RITUAL_CHANNELER_3 = 48275, // Ritual Channeler spells SPELL_PARALYZE = 48278, SPELL_SHADOWS_IN_THE_DARK = 59407, // Scourge Hulk spells SPELL_MIGHTY_BLOW = 48697, SPELL_VOLATILE_INFECTION = 56785, H_SPELL_VOLATILE_INFECTION = 59228 }; enum Yells { // Svala SAY_SVALA_INTRO_0 = 0, // Svala Sorrowgrave SAY_SVALA_INTRO_1 = 0, SAY_SVALA_INTRO_2 = 1, SAY_AGGRO = 2, SAY_SLAY = 3, SAY_DEATH = 4, SAY_SACRIFICE_PLAYER = 5, // Image of Arthas SAY_DIALOG_OF_ARTHAS_1 = 0, SAY_DIALOG_OF_ARTHAS_2 = 1 }; enum Creatures { CREATURE_ARTHAS = 29280, // Image of Arthas CREATURE_SVALA_SORROWGRAVE = 26668, // Svala after transformation CREATURE_SVALA = 29281, // Svala before transformation CREATURE_RITUAL_CHANNELER = 27281, CREATURE_SPECTATOR = 26667, CREATURE_RITUAL_TARGET = 27327, CREATURE_FLAME_BRAZIER = 27273, CREATURE_SCOURGE_HULK = 26555 }; enum Objects { OBJECT_UTGARDE_MIRROR = 191745 }; enum SvalaPhase { IDLE, INTRO, NORMAL, SACRIFICING, SVALADEAD }; #define DATA_INCREDIBLE_HULK 2043 static const float spectatorWP[2][3] = { {296.95f,-312.76f,86.36f}, {297.69f,-275.81f,86.36f} }; static Position ArthasPos = { 295.81f, -366.16f, 92.57f, 1.58f }; class boss_svala : public CreatureScript { public: boss_svala() : CreatureScript("boss_svala") { } CreatureAI* GetAI(Creature* creature) const { return new boss_svalaAI (creature); } struct boss_svalaAI : public ScriptedAI { boss_svalaAI(Creature* creature) : ScriptedAI(creature), summons(creature) { instance = creature->GetInstanceScript(); Phase = IDLE; me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_RITUAL_STRIKE_EFF_1, true); me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_RITUAL_STRIKE_EFF_2, true); } InstanceScript* instance; SummonList summons; SvalaPhase Phase; Position pos; float x, y, z; uint32 introTimer; uint8 introPhase; uint8 sacrePhase; TempSummon* arthas; uint64 arthasGUID; uint32 sinsterStrikeTimer; uint32 callFlamesTimer; uint32 sacrificeTimer; bool sacrificed75; bool sacrificed50; bool sacrificed25; bool sacrificenow; void Reset() { sacrificed75 = false; sacrificed50 = false; sacrificed25 = false; sacrificenow = false; SetCombatMovement(true); summons.DespawnAll(); me->RemoveAllAuras(); if (Phase > NORMAL) Phase = NORMAL; me->SetDisableGravity(Phase == NORMAL); introTimer = 1 * IN_MILLISECONDS; introPhase = 0; arthasGUID = 0; if (instance) { instance->SetData(DATA_SVALA_SORROWGRAVE_EVENT, NOT_STARTED); instance->SetData64(DATA_SACRIFICED_PLAYER, 0); } } void EnterCombat(Unit* /*who*/) { Talk(SAY_AGGRO); sinsterStrikeTimer = 7 * IN_MILLISECONDS; callFlamesTimer = urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); if (instance) instance->SetData(DATA_SVALA_SORROWGRAVE_EVENT, IN_PROGRESS); } void JustSummoned(Creature* summon) { if (summon->GetEntry() == CREATURE_RITUAL_CHANNELER) summon->CastSpell(summon, SPELL_SUMMONED_VIS, true); summons.Summon(summon); } void SummonedCreatureDespawn(Creature* summon) { summons.Despawn(summon); } void MoveInLineOfSight(Unit* who) { if (!who) return; if (Phase == IDLE && me->IsValidAttackTarget(who) && me->IsWithinDistInMap(who, 40)) { Phase = INTRO; me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); if (GameObject* mirror = GetClosestGameObjectWithEntry(me, OBJECT_UTGARDE_MIRROR, 100.0f)) mirror->SetGoState(GO_STATE_READY); if (Creature* arthas = me->SummonCreature(CREATURE_ARTHAS, ArthasPos, TEMPSUMMON_MANUAL_DESPAWN)) { arthas->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); arthasGUID = arthas->GetGUID(); } } } void KilledUnit(Unit* victim) { if (victim != me) Talk(SAY_SLAY); } void JustDied(Unit* /*killer*/) { if (Phase == SACRIFICING) SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE); me->HandleEmoteCommand(EMOTE_ONESHOT_FLYDEATH); summons.DespawnAll(); if (instance) instance->SetData(DATA_SVALA_SORROWGRAVE_EVENT, DONE); Talk(SAY_DEATH); } void SpellHitTarget(Unit* /*target*/, const SpellInfo* spell) { if (spell->Id == SPELL_RITUAL_STRIKE_EFF_1 && Phase != NORMAL && Phase != SVALADEAD) { Phase = NORMAL; SetCombatMovement(true); if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 300.0f, true)) me->GetMotionMaster()->MoveChase(target); } } void UpdateAI(const uint32 diff) { switch (Phase) { case IDLE: return; case INTRO: if (introTimer <= diff) { Creature* arthas = Unit::GetCreature(*me, arthasGUID); if (!arthas) return; switch (introPhase) { case 0: Talk(SAY_SVALA_INTRO_0); ++introPhase; introTimer = 8100; break; case 1: arthas->AI()->Talk(SAY_DIALOG_OF_ARTHAS_1); ++introPhase; introTimer = 10000; break; case 2: arthas->CastSpell(me, SPELL_TRANSFORMING_CHANNEL, false); pos.Relocate(me); pos.m_positionZ += 8.0f; me->GetMotionMaster()->MoveTakeoff(0, pos); // spectators flee event if (instance) { std::list lspectatorList; GetCreatureListWithEntryInGrid(lspectatorList, me, CREATURE_SPECTATOR, 100.0f); for (std::list::iterator itr = lspectatorList.begin(); itr != lspectatorList.end(); ++itr) { if ((*itr)->isAlive()) { (*itr)->SetStandState(UNIT_STAND_STATE_STAND); (*itr)->SetWalk(false); (*itr)->GetMotionMaster()->MovePoint(1, spectatorWP[0][0], spectatorWP[0][1], spectatorWP[0][2]); } } } ++introPhase; introTimer = 4200; break; case 3: me->CastSpell(me, SPELL_SVALA_TRANSFORMING1, false); ++introPhase; introTimer = 6200; break; case 4: me->CastSpell(me, SPELL_SVALA_TRANSFORMING2, false); arthas->InterruptNonMeleeSpells(true); me->RemoveAllAuras(); me->UpdateEntry(CREATURE_SVALA_SORROWGRAVE); me->SetFacingToObject(arthas); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); ++introPhase; introTimer = 3200; break; case 5: Talk(SAY_SVALA_INTRO_1); ++introPhase; introTimer = 10000; break; case 6: arthas->AI()->Talk(SAY_DIALOG_OF_ARTHAS_2); ++introPhase; introTimer = 7200; break; case 7: Talk(SAY_SVALA_INTRO_2); me->SetOrientation(1.58f); me->SendMovementFlagUpdate(); arthas->SetVisible(false); ++introPhase; introTimer = 13800; break; case 8: pos.Relocate(me); pos.m_positionX = me->GetHomePosition().GetPositionX(); pos.m_positionY = me->GetHomePosition().GetPositionY(); pos.m_positionZ = 90.6065f; me->GetMotionMaster()->MoveLand(0, pos); me->SetDisableGravity(false, true); me->SetHover(true); ++introPhase; introTimer = 3000; break; case 9: if (GameObject* mirror = GetClosestGameObjectWithEntry(me, OBJECT_UTGARDE_MIRROR, 100.0f)) mirror->SetGoState(GO_STATE_ACTIVE); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); arthas->DespawnOrUnsummon(); arthasGUID = 0; Phase = NORMAL; break; } } else introTimer -= diff; return; case NORMAL: //Return since we have no target if (!UpdateVictim()) return; if (sinsterStrikeTimer <= diff) { DoCast(me->getVictim(), SPELL_SINSTER_STRIKE); sinsterStrikeTimer = urand(5 * IN_MILLISECONDS, 9 * IN_MILLISECONDS); } else sinsterStrikeTimer -= diff; if (callFlamesTimer <= diff) { if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) { DoCast(target, SPELL_CALL_FLAMES); callFlamesTimer = urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS); } } else callFlamesTimer -= diff; //RITUAL_OF_THE_SWORD at 75%, 50% and 25% if ( (!sacrificed75 && HealthBelowPct(75))) { sacrificenow = true; sacrificed75 = true; } if ( (!sacrificed50 && HealthBelowPct(50))) { sacrificenow = true; sacrificed50 = true; } if ( (!sacrificed25 && HealthBelowPct(25))) { sacrificenow = true; sacrificed25 = true; } //PREPARING for SACRIFICING_PHASE if (sacrificenow) { if (Unit* sacrificeTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 80.0f, true)) { if (instance) instance->SetData64(DATA_SACRIFICED_PLAYER, sacrificeTarget->GetGUID()); Talk(SAY_SACRIFICE_PLAYER); DoCast(sacrificeTarget, SPELL_RITUAL_PREPARATION); SetCombatMovement(false); Phase = SACRIFICING; sacrePhase = 0; sacrificeTimer = 1 * IN_MILLISECONDS; DoCast(me, SPELL_RITUAL_OF_THE_SWORD); } } DoMeleeAttackIfReady(); break; case SACRIFICING: if (sacrificeTimer <= diff) { switch (sacrePhase) { case 0: // spawn ritual channelers if (instance) { DoCast(me, SPELL_RITUAL_CHANNELER_1, true); DoCast(me, SPELL_RITUAL_CHANNELER_2, true); DoCast(me, SPELL_RITUAL_CHANNELER_3, true); } ++sacrePhase; sacrificeTimer = 2 * IN_MILLISECONDS; break; case 1: me->StopMoving(); me->GetMotionMaster()->MoveIdle(); me->InterruptNonMeleeSpells(true); DoCast(me, SPELL_RITUAL_STRIKE_TRIGGER, true); ++sacrePhase; sacrificeTimer = 200; break; case 2: DoCast(me, SPELL_RITUAL_DISARM); ++sacrePhase; break; case 3: //resetting sacrificenow after the SACRIFICE Phase is over sacrificenow = false; break; } } else { sacrificeTimer -= diff; } break; default: break; } } }; }; class npc_ritual_channeler : public CreatureScript { public: npc_ritual_channeler() : CreatureScript("npc_ritual_channeler") { } CreatureAI* GetAI(Creature* creature) const { return new npc_ritual_channelerAI(creature); } struct npc_ritual_channelerAI : public Scripted_NoMovementAI { npc_ritual_channelerAI(Creature* creature) :Scripted_NoMovementAI(creature) { instance = creature->GetInstanceScript(); } InstanceScript* instance; uint32 paralyzeTimer; void Reset() { paralyzeTimer = 1600; if (instance) if (IsHeroic()) DoCast(me, SPELL_SHADOWS_IN_THE_DARK); } void UpdateAI(const uint32 diff) { if (me->HasUnitState(UNIT_STATE_CASTING)) return; if (paralyzeTimer <= diff) { if (instance) if (Unit* victim = me->GetUnit(*me, instance->GetData64(DATA_SACRIFICED_PLAYER))) DoCast(victim, SPELL_PARALYZE, false); paralyzeTimer = 200; } else paralyzeTimer -= diff; } }; }; class npc_spectator : public CreatureScript { public: npc_spectator() : CreatureScript("npc_spectator") { } CreatureAI* GetAI(Creature* creature) const { return new npc_spectatorAI(creature); } struct npc_spectatorAI : public ScriptedAI { npc_spectatorAI(Creature* creature) : ScriptedAI(creature) { } void Reset() { } void MovementInform(uint32 motionType, uint32 pointId) { if (motionType == POINT_MOTION_TYPE) { if (pointId == 1) me->GetMotionMaster()->MovePoint(2,spectatorWP[1][0],spectatorWP[1][1],spectatorWP[1][2]); else if (pointId == 2) me->DespawnOrUnsummon(1000); } } }; }; class RitualTargetCheck { public: explicit RitualTargetCheck(Unit* _caster) : caster(_caster) { } bool operator() (WorldObject* unit) const { if (InstanceScript* instance = caster->GetInstanceScript()) if (instance->GetData64(DATA_SACRIFICED_PLAYER) == unit->GetGUID()) return false; return true; } private: Unit* caster; }; class spell_paralyze_pinnacle : public SpellScriptLoader { public: spell_paralyze_pinnacle() : SpellScriptLoader("spell_paralyze_pinnacle") { } class spell_paralyze_pinnacle_SpellScript : public SpellScript { PrepareSpellScript(spell_paralyze_pinnacle_SpellScript); void FilterTargets(std::list& unitList) { unitList.remove_if(RitualTargetCheck(GetCaster())); } void Register() { OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_paralyze_pinnacle_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); } }; SpellScript* GetSpellScript() const { return new spell_paralyze_pinnacle_SpellScript(); } }; class npc_scourge_hulk : public CreatureScript { public: npc_scourge_hulk() : CreatureScript("npc_scourge_hulk") { } struct npc_scourge_hulkAI : public ScriptedAI { npc_scourge_hulkAI(Creature* creature) : ScriptedAI(creature) { } uint32 mightyBlow; uint32 volatileInfection; void Reset() { mightyBlow = urand(4000, 9000); volatileInfection = urand(10000, 14000); killedByRitualStrike = false; } uint32 GetData(uint32 type) { return type == DATA_INCREDIBLE_HULK ? killedByRitualStrike : 0; } void DamageTaken(Unit* attacker, uint32 &damage) { if (damage >= me->GetHealth() && attacker->GetEntry() == CREATURE_SVALA_SORROWGRAVE) killedByRitualStrike = true; } void UpdateAI(uint32 const diff) { if (!UpdateVictim()) return; if (mightyBlow <= diff) { if (Unit* victim = me->getVictim()) if (!victim->HasUnitState(UNIT_STATE_STUNNED)) // Prevent knocking back a ritual player DoCast(victim, SPELL_MIGHTY_BLOW); mightyBlow = urand(12000, 17000); } else mightyBlow -= diff; if (volatileInfection <= diff) { DoCastVictim(SPELL_VOLATILE_INFECTION); volatileInfection = urand(13000, 17000); } else volatileInfection -= diff; DoMeleeAttackIfReady(); } private: bool killedByRitualStrike; }; CreatureAI* GetAI(Creature* creature) const { return new npc_scourge_hulkAI(creature); } }; class achievement_incredible_hulk : public AchievementCriteriaScript { public: achievement_incredible_hulk() : AchievementCriteriaScript("achievement_incredible_hulk") { } bool OnCheck(Player* /*player*/, Unit* target) { return target && target->IsAIEnabled && target->GetAI()->GetData(DATA_INCREDIBLE_HULK); } }; void AddSC_boss_svala() { new boss_svala(); new npc_ritual_channeler(); new npc_spectator(); new spell_paralyze_pinnacle(); new npc_scourge_hulk(); new achievement_incredible_hulk(); }