/*
* 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 "CellImpl.h"
#include "Containers.h"
#include "GridNotifiersImpl.h"
#include "Log.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "SpellScript.h"
#include "TemporarySummon.h"
enum ExorcismSpells
{
SPELL_JULES_GOES_PRONE = 39283,
SPELL_JULES_THREATENS_AURA = 39284,
SPELL_JULES_GOES_UPRIGHT = 39294,
SPELL_JULES_VOMITS_AURA = 39295,
SPELL_BARADAS_COMMAND = 39277,
SPELL_BARADA_FALTERS = 39278,
};
enum ExorcismTexts
{
SAY_BARADA_1 = 0,
SAY_BARADA_2 = 1,
SAY_BARADA_3 = 2,
SAY_BARADA_4 = 3,
SAY_BARADA_5 = 4,
SAY_BARADA_6 = 5,
SAY_BARADA_7 = 6,
SAY_BARADA_8 = 7,
SAY_JULES_1 = 0,
SAY_JULES_2 = 1,
SAY_JULES_3 = 2,
SAY_JULES_4 = 3,
SAY_JULES_5 = 4,
};
Position const exorcismPos[11] =
{
{ -707.123f, 2751.686f, 101.592f, 4.577416f }, //Barada Waypoint-1 0
{ -710.731f, 2749.075f, 101.592f, 1.513286f }, //Barada Cast position 1
{ -710.332f, 2754.394f, 102.948f, 3.207566f }, //Jules 2
{ -714.261f, 2747.754f, 103.391f, 0.0f }, //Jules Waypoint-1 3
{ -713.113f, 2750.194f, 103.391f, 0.0f }, //Jules Waypoint-2 4
{ -710.385f, 2750.896f, 103.391f, 0.0f }, //Jules Waypoint-3 5
{ -708.309f, 2750.062f, 103.391f, 0.0f }, //Jules Waypoint-4 6
{ -707.401f, 2747.696f, 103.391f, 0.0f }, //Jules Waypoint-5 7
{ -708.591f, 2745.266f, 103.391f, 0.0f }, //Jules Waypoint-6 8
{ -710.597f, 2744.035f, 103.391f, 0.0f }, //Jules Waypoint-7 9
{ -713.089f, 2745.302f, 103.391f, 0.0f }, //Jules Waypoint-8 10
};
enum ExorcismMisc
{
NPC_DARKNESS_RELEASED = 22507,
NPC_FOUL_PURGE = 22506,
NPC_COLONEL_JULES = 22432,
BARADAS_GOSSIP_MESSAGE = 10683,
QUEST_THE_EXORCISM_OF_COLONEL_JULES = 10935,
ACTION_START_EVENT = 1,
ACTION_JULES_HOVER = 2,
ACTION_JULES_FLIGHT = 3,
ACTION_JULES_MOVE_HOME = 4,
};
enum ExorcismEvents
{
EVENT_BARADAS_TALK = 1,
EVENT_RESET = 2,
//Colonel Jules
EVENT_SUMMON_SKULL = 1,
};
/*######
## npc_colonel_jules
######*/
class npc_colonel_jules : public CreatureScript
{
public:
npc_colonel_jules() : CreatureScript("npc_colonel_jules") { }
struct npc_colonel_julesAI : public ScriptedAI
{
npc_colonel_julesAI(Creature* creature) : ScriptedAI(creature), summons(me)
{
Initialize();
}
void Initialize()
{
point = 3;
wpreached = false;
success = false;
}
void Reset() override
{
events.Reset();
summons.DespawnAll();
Initialize();
}
bool success;
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_JULES_HOVER:
me->AddAura(SPELL_JULES_GOES_PRONE, me);
me->AddAura(SPELL_JULES_THREATENS_AURA, me);
me->SetCanFly(true);
me->SetSpeedRate(MOVE_RUN, 0.2f);
me->SetFacingTo(3.207566f);
me->GetMotionMaster()->MoveJump(exorcismPos[2], 2.0f, 2.0f);
success = false;
events.ScheduleEvent(EVENT_SUMMON_SKULL, 10s);
break;
case ACTION_JULES_FLIGHT:
me->RemoveAura(SPELL_JULES_GOES_PRONE);
me->AddAura(SPELL_JULES_GOES_UPRIGHT, me);
me->AddAura(SPELL_JULES_VOMITS_AURA, me);
wpreached = true;
me->GetMotionMaster()->MovePoint(point, exorcismPos[point]);
break;
case ACTION_JULES_MOVE_HOME:
wpreached = false;
me->SetSpeedRate(MOVE_RUN, 1.0f);
me->GetMotionMaster()->MovePoint(11, exorcismPos[2]);
events.CancelEvent(EVENT_SUMMON_SKULL);
break;
}
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->GetMotionMaster()->MoveRandom(10.0f);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (id < 10)
wpreached = true;
if (id == 8)
{
for (uint8 i = 0; i < 2; i++)
DoSummon(NPC_FOUL_PURGE, exorcismPos[8]);
}
if (id == 10)
{
wpreached = true;
point = 3;
}
}
void UpdateAI(uint32 diff) override
{
if (wpreached)
{
me->GetMotionMaster()->MovePoint(point, exorcismPos[point]);
point++;
wpreached = false;
}
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_SUMMON_SKULL:
uint8 summonCount = urand(1, 3);
for (uint8 i = 0; i < summonCount; i++)
me->SummonCreature(NPC_DARKNESS_RELEASED, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 1.5f, 0, TEMPSUMMON_MANUAL_DESPAWN);
events.ScheduleEvent(EVENT_SUMMON_SKULL, 10s, 15s);
break;
}
}
}
bool OnGossipHello(Player* player) override
{
if (success)
player->KilledMonsterCredit(NPC_COLONEL_JULES, ObjectGuid::Empty);
SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID());
return true;
}
private:
EventMap events;
SummonList summons;
uint8 point;
bool wpreached;
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_colonel_julesAI(creature);
}
};
/*######
## npc_barada
######*/
class npc_barada : public CreatureScript
{
public:
npc_barada() : CreatureScript("npc_barada") { }
struct npc_baradaAI : public ScriptedAI
{
npc_baradaAI(Creature* creature) : ScriptedAI(creature)
{
Initialize();
}
void Initialize()
{
step = 0;
}
void Reset() override
{
events.Reset();
Initialize();
playerGUID.Clear();
me->RemoveUnitFlag(UNIT_FLAG_PACIFIED);
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}
bool OnGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override
{
ClearGossipMenuFor(player);
switch (gossipListId)
{
case 1:
CloseGossipMenuFor(player);
me->AI()->Talk(SAY_BARADA_1);
me->AI()->DoAction(ACTION_START_EVENT);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
break;
default:
break;
}
return false;
}
void DoAction(int32 action) override
{
if (action == ACTION_START_EVENT)
{
if (Creature* jules = me->FindNearestCreature(NPC_COLONEL_JULES, 20.0f, true))
{
julesGUID = jules->GetGUID();
jules->AI()->Talk(SAY_JULES_1);
}
me->GetMotionMaster()->MovePoint(0, exorcismPos[1]);
Talk(SAY_BARADA_2);
me->SetUnitFlag(UNIT_FLAG_PACIFIED);
}
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (id == 0)
me->GetMotionMaster()->MovePoint(1, exorcismPos[1]);
if (id == 1)
events.ScheduleEvent(EVENT_BARADAS_TALK, 2s);
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
{
jules->AI()->DoAction(ACTION_JULES_MOVE_HOME);
jules->RemoveAllAuras();
}
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_BARADAS_TALK:
switch (step)
{
case 0:
me->SetFacingTo(1.513286f);
me->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL);
events.ScheduleEvent(EVENT_BARADAS_TALK, 3s);
step++;
break;
case 1:
DoCast(SPELL_BARADAS_COMMAND);
events.ScheduleEvent(EVENT_BARADAS_TALK, 5s);
step++;
break;
case 2:
Talk(SAY_BARADA_3);
events.ScheduleEvent(EVENT_BARADAS_TALK, 7s);
step++;
break;
case 3:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->Talk(SAY_JULES_2);
events.ScheduleEvent(EVENT_BARADAS_TALK, 18s);
step++;
break;
case 4:
DoCast(SPELL_BARADA_FALTERS);
me->HandleEmoteCommand(EMOTE_STAND_STATE_NONE);
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->DoAction(ACTION_JULES_HOVER);
events.ScheduleEvent(EVENT_BARADAS_TALK, 11s);
step++;
break;
case 5:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->Talk(SAY_JULES_3);
events.ScheduleEvent(EVENT_BARADAS_TALK, 13s);
step++;
break;
case 6:
Talk(SAY_BARADA_4);
events.ScheduleEvent(EVENT_BARADAS_TALK, 5s);
step++;
break;
case 7:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->Talk(SAY_JULES_3);
events.ScheduleEvent(EVENT_BARADAS_TALK, 13s);
step++;
break;
case 8:
Talk(SAY_BARADA_4);
events.ScheduleEvent(EVENT_BARADAS_TALK, 12s);
step++;
break;
case 9:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->Talk(SAY_JULES_4);
events.ScheduleEvent(EVENT_BARADAS_TALK, 12s);
step++;
break;
case 10:
Talk(SAY_BARADA_4);
events.ScheduleEvent(EVENT_BARADAS_TALK, 5s);
step++;
break;
case 11:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->DoAction(ACTION_JULES_FLIGHT);
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 12:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->Talk(SAY_JULES_4);
events.ScheduleEvent(EVENT_BARADAS_TALK, 8s);
step++;
break;
case 13:
Talk(SAY_BARADA_5);
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 14:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->Talk(SAY_JULES_4);
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 15:
Talk(SAY_BARADA_6);
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 16:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->Talk(SAY_JULES_5);
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 17:
Talk(SAY_BARADA_7);
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 18:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
jules->AI()->Talk(SAY_JULES_3);
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 19:
Talk(SAY_BARADA_7);
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 20:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
{
jules->AI()->DoAction(ACTION_JULES_MOVE_HOME);
jules->RemoveAura(SPELL_JULES_VOMITS_AURA);
}
events.ScheduleEvent(EVENT_BARADAS_TALK, 10s);
step++;
break;
case 21:
//End
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
{
ENSURE_AI(npc_colonel_jules::npc_colonel_julesAI, jules->AI())->success = true;
jules->RemoveAllAuras();
}
me->RemoveAura(SPELL_BARADAS_COMMAND);
me->RemoveUnitFlag(UNIT_FLAG_PACIFIED);
Talk(SAY_BARADA_8);
me->GetMotionMaster()->MoveTargetedHome();
EnterEvadeMode();
events.ScheduleEvent(EVENT_RESET, 2min);
break;
}
break;
case EVENT_RESET:
if (Creature* jules = ObjectAccessor::GetCreature(*me, julesGUID))
ENSURE_AI(npc_colonel_jules::npc_colonel_julesAI, jules->AI())->success = false;
break;
}
}
}
private:
EventMap events;
uint8 step;
ObjectGuid julesGUID;
ObjectGuid playerGUID;
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_baradaAI(creature);
}
};
enum Aledis
{
SAY_CHALLENGE = 0,
SAY_DEFEATED = 1,
EVENT_TALK = 1,
EVENT_ATTACK = 2,
EVENT_EVADE = 3,
EVENT_FIREBALL = 4,
EVENT_FROSTNOVA = 5,
SPELL_FIREBALL = 20823,
SPELL_FROSTNOVA = 11831
};
class npc_magister_aledis : public CreatureScript
{
public:
npc_magister_aledis() : CreatureScript("npc_magister_aledis") { }
struct npc_magister_aledisAI : public ScriptedAI
{
npc_magister_aledisAI(Creature* creature) : ScriptedAI(creature) { }
void StartFight(Player* player)
{
me->Dismount();
me->SetFacingToObject(player);
me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
_playerGUID = player->GetGUID();
_events.ScheduleEvent(EVENT_TALK, 2s);
}
void Reset() override
{
me->RestoreFaction();
me->RemoveNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
me->SetImmuneToPC(true);
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (damage > me->GetHealth() || me->HealthBelowPctDamaged(20, damage))
{
damage = 0;
_events.Reset();
me->RestoreFaction();
me->RemoveAllAuras();
me->CombatStop(true);
EngagementOver();
me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER);
me->SetImmuneToPC(true);
Talk(SAY_DEFEATED);
_events.ScheduleEvent(EVENT_EVADE, 1min);
}
}
void UpdateAI(uint32 diff) override
{
_events.Update(diff);
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_TALK:
Talk(SAY_CHALLENGE);
_events.ScheduleEvent(EVENT_ATTACK, 2s);
break;
case EVENT_ATTACK:
me->SetImmuneToPC(false);
me->SetFaction(FACTION_MONSTER_2);
if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID))
me->EngageWithTarget(player);
_events.ScheduleEvent(EVENT_FIREBALL, 1ms);
_events.ScheduleEvent(EVENT_FROSTNOVA, 5s);
break;
case EVENT_FIREBALL:
DoCast(SPELL_FIREBALL);
_events.ScheduleEvent(EVENT_FIREBALL, 10s);
break;
case EVENT_FROSTNOVA:
DoCastAOE(SPELL_FROSTNOVA);
_events.ScheduleEvent(EVENT_FROSTNOVA, 20s);
break;
case EVENT_EVADE:
EnterEvadeMode();
break;
}
}
if (!UpdateVictim())
return;
}
bool OnGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
CloseGossipMenuFor(player);
me->StopMoving();
StartFight(player);
return true;
}
private:
EventMap _events;
ObjectGuid _playerGUID;
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_magister_aledisAI(creature);
}
};
enum WatchCommanderLeonus
{
SAY_COVER = 0,
EVENT_START = 1,
EVENT_CAST = 2,
EVENT_END = 3,
GAME_EVENT_HELLFIRE = 85,
NPC_INFERNAL_RAIN = 18729,
NPC_FEAR_CONTROLLER = 19393,
SPELL_INFERNAL_RAIN = 33814,
SPELL_FEAR = 33815 // Serverside spell
};
struct npc_watch_commander_leonus : public ScriptedAI
{
npc_watch_commander_leonus(Creature* creature) : ScriptedAI(creature) { }
void OnGameEvent(bool start, uint16 eventId) override
{
if (eventId == GAME_EVENT_HELLFIRE && start)
{
_events.Reset();
_events.ScheduleEvent(EVENT_START, 1s);
}
}
void UpdateAI(uint32 diff) override
{
_events.Update(diff);
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_START:
{
Talk(SAY_COVER);
me->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT);
std::list dummies;
for (uint32 entry : { NPC_INFERNAL_RAIN, NPC_FEAR_CONTROLLER })
{
Trinity::AllCreaturesOfEntryInRange pred(me, entry);
Trinity::CreatureListSearcher searcher(me, dummies, pred);
Cell::VisitAllObjects(me, searcher, 500.0f);
}
for (Creature* dummy : dummies)
if (dummy->GetCreatureData()->movementType == 0)
dummy->AI()->SetData(EVENT_START, 0);
break;
}
}
}
if (!UpdateVictim())
return;
}
private:
EventMap _events;
};
struct npc_infernal_rain_hellfire : public ScriptedAI
{
npc_infernal_rain_hellfire(Creature* creature) : ScriptedAI(creature) { }
void SetData(uint32 type, uint32 /*data*/) override
{
if (type != EVENT_START)
return;
RebuildTargetList();
_events.ScheduleEvent(EVENT_CAST, 0s, 1s);
_events.ScheduleEvent(EVENT_END, 1min);
}
void RebuildTargetList()
{
_targets.clear();
std::vector others;
Trinity::AllCreaturesOfEntryInRange pred(me, NPC_INFERNAL_RAIN);
Trinity::CreatureListSearcher searcher(me, others, pred);
Cell::VisitAllObjects(me, searcher, 500.0f);
for (Creature* other : others)
if (other->GetCreatureData()->movementType == 2)
_targets.push_back(other->GetGUID());
}
void UpdateAI(uint32 diff) override
{
_events.Update(diff);
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_CAST:
{
if (Creature* target = ObjectAccessor::GetCreature(*me, Trinity::Containers::SelectRandomContainerElement(_targets)))
{
CastSpellExtraArgs args;
args.AddSpellMod(SPELLVALUE_MAX_TARGETS, 1);
me->CastSpell(target, SPELL_INFERNAL_RAIN, args);
}
_events.Repeat(1s, 2s);
break;
}
case EVENT_END:
_events.Reset();
break;
}
}
}
private:
EventMap _events;
std::vector _targets;
};
struct npc_fear_controller : public ScriptedAI
{
npc_fear_controller(Creature* creature) : ScriptedAI(creature) { }
void SetData(uint32 type, uint32 /*data*/) override
{
if (type != EVENT_START)
return;
_events.ScheduleEvent(EVENT_CAST, 0s, 1s);
_events.ScheduleEvent(EVENT_END, 1min);
}
void UpdateAI(uint32 diff) override
{
_events.Update(diff);
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_CAST:
DoCastAOE(SPELL_FEAR);
_events.Repeat(10s);
break;
case EVENT_END:
_events.Reset();
break;
}
}
}
private:
EventMap _events;
};
/*######
## Quest 10909: Fel Spirits
######*/
enum FelSpirits
{
SPELL_SEND_VENGEANCE_TO_PLAYER = 39202,
SPELL_SUMMON_FEL_SPIRIT = 39206
};
// 39190 - Send Vengeance
class spell_hellfire_peninsula_send_vengeance : public SpellScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SEND_VENGEANCE_TO_PLAYER });
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
if (TempSummon* target = GetHitUnit()->ToTempSummon())
if (Unit* summoner = target->GetSummonerUnit())
target->CastSpell(summoner, SPELL_SEND_VENGEANCE_TO_PLAYER, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_hellfire_peninsula_send_vengeance::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 39202 - Send Vengeance to Player
class spell_hellfire_peninsula_send_vengeance_to_player : public SpellScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SUMMON_FEL_SPIRIT });
}
void HandleScript(SpellEffIndex /*effIndex*/)
{
GetHitUnit()->CastSpell(GetHitUnit(), SPELL_SUMMON_FEL_SPIRIT, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_hellfire_peninsula_send_vengeance_to_player::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
void AddSC_hellfire_peninsula()
{
new npc_colonel_jules();
new npc_barada();
new npc_magister_aledis();
RegisterCreatureAI(npc_watch_commander_leonus);
RegisterCreatureAI(npc_infernal_rain_hellfire);
RegisterCreatureAI(npc_fear_controller);
RegisterSpellScript(spell_hellfire_peninsula_send_vengeance);
RegisterSpellScript(spell_hellfire_peninsula_send_vengeance_to_player);
}