/* * This file is part of the AzerothCore 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 "karazhan.h" #include "AreaTriggerScript.h" #include "CreatureScript.h" #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" #include "ScriptedGossip.h" #include "SpellAuraEffects.h" #include "SpellScript.h" #include "SpellScriptLoader.h" enum Spells { // Barnes SPELL_SPOTLIGHT = 25824, SPELL_TUXEDO = 32616, // Berthold SPELL_TELEPORT = 39567, // Image of Medivh SPELL_FIRE_BALL = 30967, SPELL_UBER_FIREBALL = 30971, SPELL_CONFLAGRATION_BLAST = 30977, SPELL_MANA_SHIELD = 31635, // Wrath of the Titans SPELL_WRATH_OF_THE_TITANS = 30554, SPELL_WRATH_PROC_BLAST = 30605, SPELL_WRATH_PROC_BOLT = 30606, SPELL_WRATH_PROC_FLAME = 30607, SPELL_WRATH_PROC_SPITE = 30608, SPELL_WRATH_PROC_CHILL = 30609, }; enum Creatures { NPC_ARCANAGOS = 17652, NPC_SPOTLIGHT = 19525 }; /*###### # npc_barnesAI ######*/ enum Misc { OZ_GOSSIP1_MID = 7421, // I'm not an actor. OZ_GOSSIP1_OID = 0, OZ_GOSSIP2_MID = 7422, // Ok, I'll give it a try, then. OZ_GOSSIP2_OID = 0, }; enum NPCTexts { BARNES_TEXT_NOT_READY = 8969, BARNES_TEXT_IS_READY = 8970, BARNES_TEXT_IS_READY2 = 8971, BARNES_TEXT_WIPED = 8975 }; #define OZ_GM_GOSSIP1 "[GM] Change event to EVENT_OZ" #define OZ_GM_GOSSIP2 "[GM] Change event to EVENT_HOOD" #define OZ_GM_GOSSIP3 "[GM] Change event to EVENT_RAJ" struct Dialogue { int32 textid; uint32 timer; }; static Dialogue OzDialogue[] = { {0, 6000}, {1, 18000}, {2, 9000}, {3, 15000} }; static Dialogue HoodDialogue[] = { {4, 6000}, {5, 10000}, {6, 14000}, {7, 15000} }; static Dialogue RAJDialogue[] = { {8, 5000}, {9, 7000}, {10, 14000}, {11, 14000} }; // Entries and spawn locations for creatures in Oz event float Spawns[6][2] = { {17535, -10896}, // Dorothee {17546, -10891}, // Roar {17547, -10884}, // Tinhead {17543, -10902}, // Strawman {17603, -10892}, // Grandmother {17534, -10900}, // Julianne }; #define SPAWN_Z 90.5f #define SPAWN_Y -1758 #define SPAWN_O 4.738f class npc_barnes : public CreatureScript { public: npc_barnes() : CreatureScript("npc_barnes") { } struct npc_barnesAI : public npc_escortAI { npc_barnesAI(Creature* creature) : npc_escortAI(creature) { m_uiEventId = 0; instance = creature->GetInstanceScript(); } InstanceScript* instance; ObjectGuid m_uiSpotlightGUID; uint32 TalkCount; uint32 TalkTimer; uint32 m_uiEventId; bool PerformanceReady; void Reset() override { m_uiSpotlightGUID.Clear(); TalkCount = 0; TalkTimer = 2000; PerformanceReady = false; m_uiEventId = instance->GetData(DATA_OPERA_PERFORMANCE); } void StartEvent() { instance->SetBossState(DATA_OPERA_PERFORMANCE, IN_PROGRESS); //resets count for this event, in case earlier failed if (m_uiEventId == EVENT_OZ) instance->SetData(DATA_OPERA_OZ_DEATHCOUNT, IN_PROGRESS); me->SetWalk(true); Start(false); } void JustEngagedWith(Unit* /*who*/) override { } void WaypointReached(uint32 waypointId) override { switch (waypointId) { case 0: DoCastSelf(SPELL_TUXEDO); instance->HandleGameObject(instance->GetGuidData(DATA_GO_STAGEDOORLEFT), true); break; case 4: TalkCount = 0; SetEscortPaused(true); if (Creature* spotlight = me->SummonCreature(NPC_SPOTLIGHT, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000)) { spotlight->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); spotlight->CastSpell(spotlight, SPELL_SPOTLIGHT, false); m_uiSpotlightGUID = spotlight->GetGUID(); } break; case 8: if (m_uiEventId != EVENT_HOOD) // in red riding hood door should close when gossip with grandma is over { instance->DoUseDoorOrButton(instance->GetGuidData(DATA_GO_STAGEDOORLEFT)); } PerformanceReady = true; break; case 9: PrepareEncounter(); instance->DoUseDoorOrButton(instance->GetGuidData(DATA_GO_CURTAINS)); break; } } void Talk(uint32 count) { int32 text = 0; switch (m_uiEventId) { case EVENT_OZ: text = OzDialogue[count].textid; TalkTimer = OzDialogue[count].timer; break; case EVENT_HOOD: text = HoodDialogue[count].textid; TalkTimer = HoodDialogue[count].timer; break; case EVENT_RAJ: text = RAJDialogue[count].textid; TalkTimer = RAJDialogue[count].timer; break; default: return; } CreatureAI::Talk(text); } void PrepareEncounter() { LOG_DEBUG("scripts.ai", "Barnes Opera Event - Introduction complete - preparing encounter {}", m_uiEventId); uint8 index = 0; uint8 count = 0; switch (m_uiEventId) { case EVENT_OZ: index = 0; count = 4; break; case EVENT_HOOD: index = 4; count = index + 1; break; case EVENT_RAJ: index = 5; count = index + 1; break; } for (; index < count; ++index) { uint32 entry = ((uint32)Spawns[index][0]); float PosX = Spawns[index][1]; if (Creature* creature = me->SummonCreature(entry, PosX, SPAWN_Y, SPAWN_Z, SPAWN_O, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR * 2 * IN_MILLISECONDS)) creature->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); } instance->SetData(DATA_SPAWN_OPERA_DECORATIONS, m_uiEventId); } void UpdateAI(uint32 diff) override { npc_escortAI::UpdateAI(diff); if (HasEscortState(STATE_ESCORT_PAUSED)) { if (TalkTimer <= diff) { if (TalkCount > 3) { if (Creature* pSpotlight = ObjectAccessor::GetCreature(*me, m_uiSpotlightGUID)) pSpotlight->DespawnOrUnsummon(); SetEscortPaused(false); return; } Talk(TalkCount); ++TalkCount; } else TalkTimer -= diff; } } }; bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override { ClearGossipMenuFor(player); npc_barnesAI* pBarnesAI = CAST_AI(npc_barnes::npc_barnesAI, creature->AI()); switch (action) { case GOSSIP_ACTION_INFO_DEF+1: AddGossipItemFor(player, OZ_GOSSIP2_MID, OZ_GOSSIP2_OID, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); SendGossipMenuFor(player, BARNES_TEXT_IS_READY2, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF+2: CloseGossipMenuFor(player); pBarnesAI->StartEvent(); break; case GOSSIP_ACTION_INFO_DEF+3: CloseGossipMenuFor(player); pBarnesAI->m_uiEventId = EVENT_OZ; break; case GOSSIP_ACTION_INFO_DEF+4: CloseGossipMenuFor(player); pBarnesAI->m_uiEventId = EVENT_HOOD; break; case GOSSIP_ACTION_INFO_DEF+5: CloseGossipMenuFor(player); pBarnesAI->m_uiEventId = EVENT_RAJ; break; } return true; } bool OnGossipHello(Player* player, Creature* creature) override { if (InstanceScript* instance = creature->GetInstanceScript()) { // Check for death of Moroes and if opera event is not done already if (instance->GetBossState(DATA_MOROES) == DONE && instance->GetBossState(DATA_OPERA_PERFORMANCE) != DONE) { AddGossipItemFor(player, OZ_GOSSIP1_MID, OZ_GOSSIP1_OID, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); if (player->IsGameMaster()) { AddGossipItemFor(player, GOSSIP_ICON_DOT, OZ_GM_GOSSIP1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); AddGossipItemFor(player, GOSSIP_ICON_DOT, OZ_GM_GOSSIP2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); AddGossipItemFor(player, GOSSIP_ICON_DOT, OZ_GM_GOSSIP3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); } if (instance->GetBossState(DATA_OPERA_PERFORMANCE) != FAIL) { SendGossipMenuFor(player, BARNES_TEXT_IS_READY, creature->GetGUID()); } else { SendGossipMenuFor(player, BARNES_TEXT_WIPED, creature->GetGUID()); } return true; } } SendGossipMenuFor(player, BARNES_TEXT_NOT_READY, creature->GetGUID()); return true; } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } }; /*### # npc_image_of_medivh ####*/ enum MedivhTexts { SAY_DIALOG_MEDIVH_1 = 0, SAY_DIALOG_ARCANAGOS_2 = 0, SAY_DIALOG_MEDIVH_3 = 1, SAY_DIALOG_ARCANAGOS_4 = 1, SAY_DIALOG_MEDIVH_5 = 2, SAY_DIALOG_ARCANAGOS_6 = 2, EMOTE_DIALOG_MEDIVH_7 = 3, SAY_DIALOG_ARCANAGOS_8 = 3, SAY_DIALOG_MEDIVH_9 = 4 }; //static float MedivPos[4] = {-11161.49f, -1902.24f, 91.48f, 1.94f}; static float ArcanagosPos[4] = {-11169.75f, -1881.48f, 107.39f, 4.83f}; class npc_image_of_medivh : public CreatureScript { public: npc_image_of_medivh() : CreatureScript("npc_image_of_medivh") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct npc_image_of_medivhAI : public ScriptedAI { npc_image_of_medivhAI(Creature* creature) : ScriptedAI(creature) { instance = creature->GetInstanceScript(); Step = 1; YellTimer = 5000; } InstanceScript* instance; ObjectGuid ArcanagosGUID; uint32 YellTimer; uint8 Step; int32 MTimer; int32 ATimer; bool EventStarted; void Reset() override { ArcanagosGUID.Clear(); MTimer = 0; ATimer = 0; if (instance && !instance->GetGuidData(DATA_IMAGE_OF_MEDIVH)) { Creature* Arcanagos = me->SummonCreature(NPC_ARCANAGOS, ArcanagosPos[0], ArcanagosPos[1], ArcanagosPos[2], 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000); if (!Arcanagos) { me->DespawnOrUnsummon(); return; } instance->SetGuidData(DATA_IMAGE_OF_MEDIVH, me->GetGUID()); EventStarted = true; ArcanagosGUID = Arcanagos->GetGUID(); Arcanagos->SetFacingToObject(me); me->SetFacingToObject(Arcanagos); Arcanagos->SetCanFly(true); } else me->DespawnOrUnsummon(); } void JustEngagedWith(Unit* /*who*/) override {} uint32 NextStep(uint32 nextStep) { switch (nextStep) { case 1: Talk(SAY_DIALOG_MEDIVH_1); return 10000; case 2: if (Creature* arca = ObjectAccessor::GetCreature((*me), ArcanagosGUID)) arca->AI()->Talk(SAY_DIALOG_ARCANAGOS_2); return 20000; case 3: Talk(SAY_DIALOG_MEDIVH_3); return 10000; case 4: if (Creature* arca = ObjectAccessor::GetCreature((*me), ArcanagosGUID)) arca->AI()->Talk(SAY_DIALOG_ARCANAGOS_4); return 20000; case 5: Talk(SAY_DIALOG_MEDIVH_5); return 20000; case 6: if (Creature* arca = ObjectAccessor::GetCreature((*me), ArcanagosGUID)) arca->AI()->Talk(SAY_DIALOG_ARCANAGOS_6); ATimer = 5500; MTimer = 6600; return 10000; case 7: return 1000; case 8: me->CastSpell(me, SPELL_MANA_SHIELD, true); return 5500; case 9: me->TextEmote(EMOTE_DIALOG_MEDIVH_7); me->CastSpell(me, 30972, true); return 10000; case 10: me->RemoveAurasDueToSpell(30972); if (Creature* arca = ObjectAccessor::GetCreature((*me), ArcanagosGUID)) me->CastSpell(arca, SPELL_CONFLAGRATION_BLAST, false); return 1000; case 11: if (Creature* arca = ObjectAccessor::GetCreature((*me), ArcanagosGUID)) arca->AI()->Talk(SAY_DIALOG_ARCANAGOS_8); return 5000; case 12: if (Creature* arca = ObjectAccessor::GetCreature((*me), ArcanagosGUID)) { arca->SetSpeed(MOVE_RUN, 2.0f); arca->GetMotionMaster()->MovePoint(0, -11010.82f, -1761.18f, 156.47f); arca->InterruptNonMeleeSpells(true); } return 10000; case 13: Talk(SAY_DIALOG_MEDIVH_9); return 10000; case 14: if (me->GetMap()->IsDungeon()) { InstanceMap::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); for (InstanceMap::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) { if (i->GetSource()->GetQuestStatus(9645) == QUEST_STATUS_INCOMPLETE) { i->GetSource()->GroupEventHappens(9645, me); break; } } } me->DespawnOrUnsummon(100ms); if (Creature* arca = ObjectAccessor::GetCreature((*me), ArcanagosGUID)) arca->DespawnOrUnsummon(100ms); return 5000; default: return 2000; } } void UpdateAI(uint32 diff) override { if (YellTimer <= diff) { if (EventStarted) YellTimer = NextStep(Step++); } else YellTimer -= diff; if (Step >= 7 && Step <= 8) { ATimer += diff; MTimer += diff; if (ATimer >= 6000) { if (Unit* arca = ObjectAccessor::GetUnit((*me), ArcanagosGUID)) arca->CastSpell(me, SPELL_FIRE_BALL, false); ATimer = 0; } if (MTimer >= 6000) { if (Unit* arca = ObjectAccessor::GetUnit((*me), ArcanagosGUID)) me->CastSpell(arca, SPELL_FIRE_BALL, false); MTimer = 0; } } } }; }; class at_karazhan_side_entrance : public OnlyOnceAreaTriggerScript { public: at_karazhan_side_entrance() : OnlyOnceAreaTriggerScript("at_karazhan_side_entrance") { } bool _OnTrigger(Player* player, AreaTrigger const* /*at*/) override { if (InstanceScript* instance = player->GetInstanceScript()) { if (instance->GetBossState(DATA_OPERA_PERFORMANCE) == DONE) { if (GameObject* door = instance->GetGameObject(DATA_GO_SIDE_ENTRANCE_DOOR)) { instance->HandleGameObject(ObjectGuid::Empty, true, door); door->RemoveGameObjectFlag(GO_FLAG_LOCKED); } } } return false; } }; class spell_karazhan_temptation : public AuraScript { PrepareAuraScript(spell_karazhan_temptation); void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) { PreventDefaultAction(); if (eventInfo.GetActionTarget()) { GetTarget()->CastSpell(eventInfo.GetActionTarget(), GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true); } } void Register() override { OnEffectProc += AuraEffectProcFn(spell_karazhan_temptation::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } }; // 30610 - Wrath of the Titans Stacker class spell_karazhan_wrath_titans_stacker : public SpellScript { PrepareSpellScript(spell_karazhan_wrath_titans_stacker); bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_WRATH_OF_THE_TITANS }); } void HandleDummy(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); Unit* caster = GetCaster(); if (!caster) return; caster->CastSpell(caster, SPELL_WRATH_OF_THE_TITANS, true); if (Aura* aur = caster->GetAura(SPELL_WRATH_OF_THE_TITANS)) aur->SetStackAmount(5); } void Register() override { OnEffectHitTarget += SpellEffectFn(spell_karazhan_wrath_titans_stacker::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; // 30554 - Wrath of the Titans class spell_karazhan_wrath_titans_aura : public AuraScript { PrepareAuraScript(spell_karazhan_wrath_titans_aura); bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellInfo({ SPELL_WRATH_PROC_BLAST, SPELL_WRATH_PROC_BOLT, SPELL_WRATH_PROC_FLAME, SPELL_WRATH_PROC_SPITE, SPELL_WRATH_PROC_CHILL }); } bool CheckProc(ProcEventInfo& eventInfo) { if (!eventInfo.GetSpellInfo()) return false; if (GetFirstSchoolInMask(eventInfo.GetSpellInfo()->GetSchoolMask()) == SPELL_SCHOOL_NORMAL) return false; if (GetFirstSchoolInMask(eventInfo.GetSpellInfo()->GetSchoolMask()) == SPELL_SCHOOL_HOLY) return false; return true; } void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo) { PreventDefaultAction(); Unit* target = eventInfo.GetActionTarget(); Player* caster = GetTarget()->ToPlayer(); if (!target || !caster) return; uint32 spellId = 0; switch (GetFirstSchoolInMask(eventInfo.GetSpellInfo()->GetSchoolMask())) { case SPELL_SCHOOL_FIRE: spellId = SPELL_WRATH_PROC_FLAME; break; case SPELL_SCHOOL_NATURE: spellId = SPELL_WRATH_PROC_BOLT; break; case SPELL_SCHOOL_FROST: spellId = SPELL_WRATH_PROC_CHILL; break; case SPELL_SCHOOL_SHADOW: spellId = SPELL_WRATH_PROC_SPITE; break; case SPELL_SCHOOL_ARCANE: spellId = SPELL_WRATH_PROC_BLAST; break; default: return; } caster->CastSpell(target, spellId, true); ModStackAmount(-1); } void Register() override { DoCheckProc += AuraCheckProcFn(spell_karazhan_wrath_titans_aura::CheckProc); OnEffectProc += AuraEffectProcFn(spell_karazhan_wrath_titans_aura::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; void AddSC_karazhan() { new npc_barnes(); new npc_image_of_medivh(); new at_karazhan_side_entrance(); RegisterSpellScript(spell_karazhan_temptation); RegisterSpellScript(spell_karazhan_wrath_titans_stacker); RegisterSpellScript(spell_karazhan_wrath_titans_aura); }