/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * 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 "GameObject.h" #include "GameObjectAI.h" #include "InstanceScript.h" #include "karazhan.h" #include "MotionMaster.h" #include "ObjectAccessor.h" #include "ScriptedCreature.h" #include "SpellAuraEffects.h" #include "SpellScript.h" enum NightbaneSpells { SPELL_BELLOWING_ROAR = 36922, SPELL_CHARRED_EARTH = 30129, SPELL_CLEAVE = 30131, SPELL_DISTRACTING_ASH = 30130, SPELL_RAIN_OF_BONES = 37098, SPELL_SMOKING_BLAST = 30128, SPELL_SMOKING_BLAST_T = 37057, SPELL_SMOLDERING_BREATH = 30210, SPELL_SUMMON_SKELETON = 30170, SPELL_TAIL_SWEEP = 25653 }; enum Says { EMOTE_SUMMON = 0, YELL_AGGRO = 1, YELL_FLY_PHASE = 2, YELL_LAND_PHASE = 3, EMOTE_BREATH = 4 }; enum NightbanePoints { POINT_INTRO_START = 0, POINT_INTRO_END = 1, POINT_INTRO_LANDING = 2, POINT_PHASE_TWO_FLY = 3, POINT_PHASE_TWO_PRE_FLY = 4, POINT_PHASE_TWO_LANDING = 5, POINT_PHASE_TWO_END = 6 }; enum NightbaneSplineChain { SPLINE_CHAIN_INTRO_START = 1, SPLINE_CHAIN_INTRO_END = 2, SPLINE_CHAIN_INTRO_LANDING = 3, SPLINE_CHAIN_SECOND_LANDING = 4, SPLINE_CHAIN_PHASE_TWO = 5 }; enum NightbaneEvents { EVENT_BELLOWING_ROAR = 1, EVENT_CHARRED_EARTH, EVENT_CLEAVE, EVENT_DISTRACTING_ASH, EVENT_EMOTE_BREATH, EVENT_END_INTRO, EVENT_END_PHASE_TWO, EVENT_INTRO_LANDING, EVENT_LAND, EVENT_LANDED, EVENT_PRE_FLY_END, EVENT_PRE_LAND, EVENT_RAIN_OF_BONES, EVENT_SMOLDERING_BREATH, EVENT_SMOKING_BLAST, EVENT_SMOKING_BLAST_T, EVENT_START_INTRO_PATH, EVENT_TAIL_SWEEP }; enum NightbanePhases { PHASE_INTRO = 0, PHASE_GROUND, PHASE_FLY }; enum NightbaneGroups { GROUP_GROUND = 1, GROUP_FLY }; enum NightbaneMisc { ACTION_SUMMON = 0, PATH_PHASE_TWO = 13547500 }; Position const FlyPosition = { -11160.13f, -1870.683f, 97.73876f, 0.0f }; Position const FlyPositionLeft = { -11094.42f, -1866.992f, 107.8375f, 0.0f }; Position const FlyPositionRight = { -11193.77f, -1921.983f, 107.9845f, 0.0f }; class boss_nightbane : public CreatureScript { public: boss_nightbane() : CreatureScript("boss_nightbane") { } struct boss_nightbaneAI : public BossAI { boss_nightbaneAI(Creature* creature) : BossAI(creature, DATA_NIGHTBANE), _flyCount(0) { } void Reset() override { _Reset(); _flyCount = 0; me->SetDisableGravity(true); HandleTerraceDoors(true); if (GameObject* urn = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_GO_BLACKENED_URN))) urn->RemoveFlag(GO_FLAG_IN_USE); } void EnterEvadeMode(EvadeReason why) override { me->SetDisableGravity(true); CreatureAI::EnterEvadeMode(why); } void JustReachedHome() override { _DespawnAtEvade(); } void JustDied(Unit* /*killer*/) override { _JustDied(); HandleTerraceDoors(true); } void DoAction(int32 action) override { if (action == ACTION_SUMMON) { Talk(EMOTE_SUMMON); events.SetPhase(PHASE_INTRO); me->setActive(true); me->SetFarVisible(true); me->SetUninteractible(false); me->GetMotionMaster()->MoveAlongSplineChain(POINT_INTRO_START, SPLINE_CHAIN_INTRO_START, false); HandleTerraceDoors(false); } } void SetupGroundPhase() { events.SetPhase(PHASE_GROUND); events.ScheduleEvent(EVENT_CLEAVE, 0s, Seconds(15), GROUP_GROUND); events.ScheduleEvent(EVENT_TAIL_SWEEP, Seconds(4), Seconds(23), GROUP_GROUND); events.ScheduleEvent(EVENT_BELLOWING_ROAR, Seconds(48), GROUP_GROUND); events.ScheduleEvent(EVENT_CHARRED_EARTH, Seconds(12), Seconds(18), GROUP_GROUND); events.ScheduleEvent(EVENT_SMOLDERING_BREATH, Seconds(26), Seconds(30), GROUP_GROUND); events.ScheduleEvent(EVENT_DISTRACTING_ASH, Seconds(82), GROUP_GROUND); } void HandleTerraceDoors(bool open) { instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_1), open); instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_2), open); } void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); Talk(YELL_AGGRO); SetupGroundPhase(); } void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override { if (events.IsInPhase(PHASE_FLY)) { if (damage >= me->GetHealth()) damage = me->GetHealth() -1; return; } if ((_flyCount == 0 && HealthBelowPct(75)) || (_flyCount == 1 && HealthBelowPct(50)) || (_flyCount == 2 && HealthBelowPct(25))) { events.SetPhase(PHASE_FLY); StartPhaseFly(); } } void MovementInform(uint32 type, uint32 pointId) override { if (type == SPLINE_CHAIN_MOTION_TYPE) { switch (pointId) { case POINT_INTRO_START: me->SetStandState(UNIT_STAND_STATE_STAND); events.ScheduleEvent(EVENT_START_INTRO_PATH, Milliseconds(1)); break; case POINT_INTRO_END: events.ScheduleEvent(EVENT_END_INTRO, 2s); break; case POINT_INTRO_LANDING: me->SetDisableGravity(false); me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); events.ScheduleEvent(EVENT_INTRO_LANDING, 3s); break; case POINT_PHASE_TWO_LANDING: events.SetPhase(PHASE_GROUND); me->SetDisableGravity(false); me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); events.ScheduleEvent(EVENT_LANDED, 3s); break; case POINT_PHASE_TWO_END: events.ScheduleEvent(EVENT_END_PHASE_TWO, Milliseconds(1)); break; default: break; } } else if (type == POINT_MOTION_TYPE) { if (pointId == POINT_PHASE_TWO_FLY) { events.ScheduleEvent(EVENT_PRE_LAND, Seconds(33), GROUP_FLY); events.ScheduleEvent(EVENT_EMOTE_BREATH, Seconds(2), GROUP_FLY); events.ScheduleEvent(EVENT_SMOKING_BLAST_T, Seconds(21), GROUP_FLY); events.ScheduleEvent(EVENT_SMOKING_BLAST, Seconds(17), GROUP_FLY); } else if (pointId == POINT_PHASE_TWO_PRE_FLY) events.ScheduleEvent(EVENT_PRE_FLY_END, Milliseconds(1)); } } void StartPhaseFly() { ++_flyCount; Talk(YELL_FLY_PHASE); events.CancelEventGroup(GROUP_GROUND); me->InterruptNonMeleeSpells(false); me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); me->SetDisableGravity(true); me->SetReactState(REACT_PASSIVE); me->AttackStop(); if (me->GetDistance(FlyPositionLeft) < me->GetDistance(FlyPosition)) me->GetMotionMaster()->MovePoint(POINT_PHASE_TWO_PRE_FLY, FlyPositionLeft, true); else if (me->GetDistance(FlyPositionRight) < me->GetDistance(FlyPosition)) me->GetMotionMaster()->MovePoint(POINT_PHASE_TWO_PRE_FLY, FlyPositionRight, true); else me->GetMotionMaster()->MovePoint(POINT_PHASE_TWO_FLY, FlyPosition, true); } void ExecuteEvent(uint32 eventId) override { switch (eventId) { case EVENT_BELLOWING_ROAR: DoCastAOE(SPELL_BELLOWING_ROAR); break; case EVENT_CHARRED_EARTH: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) DoCast(target, SPELL_CHARRED_EARTH); events.Repeat(Seconds(18), Seconds(21)); break; case EVENT_CLEAVE: DoCastVictim(SPELL_CLEAVE); events.Repeat(Seconds(6), Seconds(15)); break; case EVENT_DISTRACTING_ASH: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) DoCast(target, SPELL_DISTRACTING_ASH); break; case EVENT_EMOTE_BREATH: Talk(EMOTE_BREATH); events.ScheduleEvent(EVENT_RAIN_OF_BONES, Seconds(3), GROUP_FLY); break; case EVENT_END_INTRO: me->GetMotionMaster()->MoveAlongSplineChain(POINT_INTRO_LANDING, SPLINE_CHAIN_INTRO_LANDING, false); break; case EVENT_END_PHASE_TWO: me->GetMotionMaster()->MoveAlongSplineChain(POINT_PHASE_TWO_LANDING, SPLINE_CHAIN_SECOND_LANDING, false); break; case EVENT_INTRO_LANDING: me->SetImmuneToPC(false); DoZoneInCombat(); break; case EVENT_LAND: Talk(YELL_LAND_PHASE); me->SetDisableGravity(true); me->GetMotionMaster()->MoveAlongSplineChain(POINT_PHASE_TWO_END, SPLINE_CHAIN_PHASE_TWO, false); break; case EVENT_LANDED: SetupGroundPhase(); me->SetReactState(REACT_AGGRESSIVE); break; case EVENT_PRE_FLY_END: me->GetMotionMaster()->MovePoint(POINT_PHASE_TWO_FLY, FlyPosition, true); break; case EVENT_PRE_LAND: events.CancelEventGroup(GROUP_FLY); events.ScheduleEvent(EVENT_LAND, Seconds(2), GROUP_GROUND); break; case EVENT_START_INTRO_PATH: me->GetMotionMaster()->MoveAlongSplineChain(POINT_INTRO_END, SPLINE_CHAIN_INTRO_END, false); break; case EVENT_RAIN_OF_BONES: ResetThreatList(); if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) { me->SetFacingToObject(target); DoCast(target, SPELL_RAIN_OF_BONES); } break; case EVENT_SMOLDERING_BREATH: DoCastVictim(SPELL_SMOLDERING_BREATH); events.Repeat(Seconds(28), Seconds(40)); break; case EVENT_SMOKING_BLAST: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) DoCast(target, SPELL_SMOKING_BLAST); events.Repeat(Milliseconds(1400)); break; case EVENT_SMOKING_BLAST_T: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) DoCast(target, SPELL_SMOKING_BLAST_T); events.Repeat(Seconds(5), Seconds(7)); break; case EVENT_TAIL_SWEEP: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true)) if (!me->HasInArc(float(M_PI), target)) DoCast(target, SPELL_TAIL_SWEEP); events.Repeat(Seconds(20), Seconds(30)); break; default: break; } } void UpdateAI(uint32 diff) override { if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) return; events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) return; while (uint32 eventId = events.ExecuteEvent()) { ExecuteEvent(eventId); if (me->HasUnitState(UNIT_STATE_CASTING)) return; } } private: uint8 _flyCount; }; CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } }; // 37098 - Rain of Bones class spell_rain_of_bones : public SpellScriptLoader { public: spell_rain_of_bones() : SpellScriptLoader("spell_rain_of_bones") { } class spell_rain_of_bones_AuraScript : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_SUMMON_SKELETON }); } void OnTrigger(AuraEffect const* aurEff) { if (aurEff->GetTickNumber() % 5 == 0) GetTarget()->CastSpell(GetTarget(), SPELL_SUMMON_SKELETON, true); } void Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_rain_of_bones_AuraScript::OnTrigger, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL); } }; AuraScript* GetAuraScript() const override { return new spell_rain_of_bones_AuraScript(); } }; class go_blackened_urn : public GameObjectScript { public: go_blackened_urn() : GameObjectScript("go_blackened_urn") { } struct go_blackened_urnAI : GameObjectAI { go_blackened_urnAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { } InstanceScript* instance; bool OnGossipHello(Player* /*player*/) override { if (me->HasFlag(GO_FLAG_IN_USE)) return false; if (instance->GetBossState(DATA_NIGHTBANE) == DONE || instance->GetBossState(DATA_NIGHTBANE) == IN_PROGRESS) return false; if (Creature* nightbane = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_NIGHTBANE))) { me->SetFlag(GO_FLAG_IN_USE); nightbane->AI()->DoAction(ACTION_SUMMON); } return false; } }; GameObjectAI* GetAI(GameObject* go) const override { return GetKarazhanAI(go); } }; void AddSC_boss_nightbane() { new boss_nightbane(); new spell_rain_of_bones(); new go_blackened_urn(); }