/*
* 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);
}