/*
* 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 "AreaTrigger.h"
#include "AreaTriggerAI.h"
#include "Conversation.h"
#include "ConversationAI.h"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "InstanceScript.h"
#include "Map.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "TemporarySummon.h"
#include "Unit.h"
#include "kings_rest.h"
enum KingsRestData
{
// Creature
NPC_SHADOW_OF_ZUL = 137020,
// Creature text
SAY_ANIMATED_GUARDIAN_ENGAGE = 0,
SAY_ZUL_PRE_BOSS_EVENT_SPAWN = 0,
// DisplayIDs
DISPLAY_INVISIBLE_ZUL = 11686,
// Spell Visuals
SPELL_VISUAL_ZUL_OPEN_GOB = 77330,
// Conversation
CONV_ZUL_KINGS_REST_INTRO = 7690,
// Spells
SPELL_ZUL_SHADOWFORM = 269058,
SPELL_ZUL_TRASH_EVENT_STATE = 269905,
SPELL_ZUL_DESPAWN_COSMETIC_TRASH = 270692,
SPELL_ZUL_SUMMON_VISUAL = 269619,
SPELL_ZUL_SUMMON_VISUAL_CHANNEL = 269623,
SPELL_SUPPRESSION_SLAM_SELECTOR = 270002,
SPELL_SUPPRESSION_SLAM_DAMAGE = 270003,
SPELL_RELEASED_INHIBITORS = 270016,
SPELL_GUST_SLASH = 269931,
SPELL_GUST_SLASH_DAMAGE = 269932,
SPELL_DEATHLY_CHILL = 269973,
SPELL_SHADOW_BOLT_VOLLEY = 269972,
SPELL_ANCESTRAL_FURY = 269976,
SPELL_VIGILANT_DEFENSE = 269928,
SPELL_MINION_OF_ZUL_FIXATE = 269936,
SPELL_BOUND_BY_SHADOW = 269935
};
constexpr Position ShadowOfZulIntroSpawnPosition = { -944.9617f, 2646.5268f, 832.8684f, 4.716575f };
// 67 - KingsRest - Trigger Intro Event with Shadow of Zul
struct at_kings_rest_trigger_intro_event_with_zul : AreaTriggerAI
{
at_kings_rest_trigger_intro_event_with_zul(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { }
void OnUnitEnter(Unit* unit) override
{
if (!unit->IsPlayer())
return;
Conversation::CreateConversation(CONV_ZUL_KINGS_REST_INTRO, unit, unit->GetPosition(), unit->GetGUID());
at->Remove();
}
};
// 7690 - Shadow of Zul - KingsRest Intro
class conversation_kings_rest_intro : public ConversationAI
{
public:
conversation_kings_rest_intro(Conversation* conversation) : ConversationAI(conversation) { }
enum KingsRestIntroConversationData
{
CONVO_ACTOR_INTRO_ZUL = 64206,
CONVO_LINE_INTRO_DOOR = 17526
};
enum KingsRestIntroEventData
{
EVENT_ZUL_OPEN_INTRO_DOOR = 1,
EVENT_ZUL_INTRO_DESPAWN,
};
void OnCreate(Unit* /*creator*/) override
{
TempSummon* shadowOfZul = conversation->SummonCreature(NPC_SHADOW_OF_ZUL, ShadowOfZulIntroSpawnPosition, TEMPSUMMON_MANUAL_DESPAWN);
if (!shadowOfZul)
return;
conversation->AddActor(CONVO_ACTOR_INTRO_ZUL, 0, shadowOfZul->GetGUID());
conversation->Start();
}
void OnStart() override
{
_events.ScheduleEvent(EVENT_ZUL_OPEN_INTRO_DOOR, conversation->GetLineEndTime(DEFAULT_LOCALE, CONVO_LINE_INTRO_DOOR));
}
void OnUpdate(uint32 diff) override
{
_events.Update(diff);
switch (_events.ExecuteEvent())
{
case EVENT_ZUL_OPEN_INTRO_DOOR:
{
Creature* shadowOfZul = conversation->GetActorCreature(0);
if (!shadowOfZul)
break;
shadowOfZul->RemoveAurasDueToSpell(SPELL_ZUL_SHADOWFORM);
_events.ScheduleEvent(EVENT_ZUL_INTRO_DESPAWN, 1s);
if (InstanceScript* instance = conversation->GetInstanceScript())
{
if (GameObject* gate = instance->GetGameObject(DATA_KINGS_REST_INTRO_DOOR))
{
gate->SetGoState(GO_STATE_ACTIVE);
gate->SetSpellVisualId(SPELL_VISUAL_ZUL_OPEN_GOB);
}
}
break;
}
case EVENT_ZUL_INTRO_DESPAWN:
{
Creature* shadowOfZul = conversation->GetActorCreature(0);
if (!shadowOfZul)
break;
shadowOfZul->SetDisplayId(DISPLAY_INVISIBLE_ZUL);
shadowOfZul->DespawnOrUnsummon(1s);
break;
}
default:
break;
}
}
private:
EventMap _events;
};
enum AnimatedGuardianEvent
{
EVENT_SUPPRESSION_SLAM = 1
};
// 133935 - Animated Guardian
struct npc_kings_rest_animated_guardian : public ScriptedAI
{
npc_kings_rest_animated_guardian(Creature* creature) : ScriptedAI(creature), _suppressionSlamCasts(0), _releasedInhibitors(false) { }
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_ANIMATED_GUARDIAN_ENGAGE);
_events.ScheduleEvent(EVENT_SUPPRESSION_SLAM, 12s);
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override
{
if (!_releasedInhibitors && me->HealthBelowPctDamaged(50, damage))
{
_releasedInhibitors = true;
_events.DelayEvents(1200ms);
DoCast(SPELL_RELEASED_INHIBITORS);
}
}
void Reset() override
{
_events.Reset();
_suppressionSlamCasts = 0;
_releasedInhibitors = false;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_SUPPRESSION_SLAM:
DoCast(SPELL_SUPPRESSION_SLAM_SELECTOR);
_suppressionSlamCasts++;
_events.ScheduleEvent(EVENT_SUPPRESSION_SLAM, _suppressionSlamCasts % 2 == 0 ? 12s : 13300ms);
break;
default:
break;
}
}
}
private:
EventMap _events;
int32 _suppressionSlamCasts;
bool _releasedInhibitors;
};
// 270002 - Suppression Slam
class spell_kings_rest_suppression_slam : public SpellScript
{
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_SUPPRESSION_SLAM_DAMAGE });
}
void HandleHit(SpellEffIndex /*effIndex*/)
{
// @TODO: visual is a bit off - requires SPELL_ATTR12_UNK11 impl
GetCaster()->CastSpell(GetHitUnit(), SPELL_SUPPRESSION_SLAM_DAMAGE, false);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_kings_rest_suppression_slam::HandleHit, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
// 269935 - Bound by Shadow
class spell_kings_rest_bound_by_shadow : public AuraScript
{
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL)
return;
GetTarget()->KillSelf();
}
void Register() override
{
AfterEffectRemove += AuraEffectRemoveFn(spell_kings_rest_bound_by_shadow::OnRemove, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB, AURA_EFFECT_HANDLE_REAL);
}
};
// 276031 - Pit of Despair
class spell_kings_rest_pit_of_despair : public AuraScript
{
void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
GetCaster()->KillSelf();
}
void Register() override
{
AfterEffectApply += AuraEffectRemoveFn(spell_kings_rest_pit_of_despair::OnApply, EFFECT_0, SPELL_AURA_MOD_FEAR, AURA_EFFECT_HANDLE_REAL);
}
};
constexpr Position ShadowOfZulSerpentBossEventSpawnPosition = { -1066.0365f, 2643.302f, 819.37024f, 5.028946f };
// 288466 - Serpentine Seal
struct go_kings_rest_serpentine_seal : public GameObjectAI
{
go_kings_rest_serpentine_seal(GameObject* go) : GameObjectAI(go) { }
bool OnGossipHello(Player* /*player*/) override
{
me->UseDoorOrButton();
_scheduler.Schedule(3s, [this](TaskContext)
{
if (TempSummon* zul = me->SummonCreature(NPC_SHADOW_OF_ZUL, ShadowOfZulSerpentBossEventSpawnPosition))
zul->SetScriptStringId("SerpentBossEvent");
});
return true;
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
}
private:
TaskScheduler _scheduler;
};
// 137020 - Shadow of Zul
struct npc_kings_rest_shadow_of_zul : public ScriptedAI
{
npc_kings_rest_shadow_of_zul(Creature* creature) : ScriptedAI(creature) { }
void JustAppeared() override
{
if (me->HasStringId("SerpentBossEvent"))
TriggerSerpentBossEvent();
}
void TriggerSerpentBossEvent()
{
Seconds delay = 1s;
_scheduler.Schedule(delay, [this](TaskContext)
{
Talk(SAY_ZUL_PRE_BOSS_EVENT_SPAWN);
me->CastSpell(nullptr, SPELL_ZUL_TRASH_EVENT_STATE, false);
me->CastSpell(nullptr, SPELL_ZUL_DESPAWN_COSMETIC_TRASH, false);
if (InstanceScript* instance = me->GetInstanceScript())
{
if (GameObject* pool = instance->GetGameObject(DATA_KINGS_REST_LIQUID_POOL))
pool->SetGoState(GO_STATE_ACTIVE);
}
});
delay += 9s;
_scheduler.Schedule(delay, [this](TaskContext)
{
std::vector spawnedCreatures;
me->GetMap()->SpawnGroupSpawn(SPAWN_GROUP_PRE_FIRST_BOSS, false, false, &spawnedCreatures);
for (WorldObject* obj : spawnedCreatures)
{
Creature* creature = obj->ToCreature();
if (!creature)
continue;
me->CastSpell(creature, SPELL_ZUL_SUMMON_VISUAL_CHANNEL, false);
}
});
delay += 5s;
_scheduler.Schedule(delay, [this](TaskContext)
{
me->RemoveAurasDueToSpell(SPELL_ZUL_SHADOWFORM);
});
delay += 1s;
_scheduler.Schedule(delay, [this](TaskContext)
{
me->SetDisplayId(DISPLAY_INVISIBLE_ZUL);
me->DespawnOrUnsummon(1s);
});
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
}
private:
TaskScheduler _scheduler;
};
struct npc_kings_rest_temple_basic : public ScriptedAI
{
npc_kings_rest_temple_basic(Creature* creature) : ScriptedAI(creature) { }
void JustAppeared() override
{
me->CastSpell(me, SPELL_ZUL_SUMMON_VISUAL, false);
}
};
enum ShadowBorneWarriorEvent
{
EVENT_GUST_SLASH = 1
};
// 134157 - Shadow-Borne Warrior
struct npc_kings_rest_shadow_borne_warrior : public npc_kings_rest_temple_basic
{
npc_kings_rest_shadow_borne_warrior(Creature* creature) : npc_kings_rest_temple_basic(creature) { }
void JustEngagedWith(Unit* /*who*/) override
{
_events.ScheduleEvent(EVENT_GUST_SLASH, 17s);
}
void Reset() override
{
_events.Reset();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_GUST_SLASH:
DoCast(SPELL_GUST_SLASH);
_events.ScheduleEvent(EVENT_GUST_SLASH, 17s);
break;
default:
break;
}
}
}
private:
EventMap _events;
};
enum ShadowBorneWitchDoctorEvent
{
EVENT_DEATHLY_CHILL = 1,
EVENT_SHADOW_BOLT_VOLLEY
};
// 134174 - Shadow-Borne Witch Doctor
struct npc_kings_rest_shadow_borne_witch_doctor : public npc_kings_rest_temple_basic
{
npc_kings_rest_shadow_borne_witch_doctor(Creature* creature) : npc_kings_rest_temple_basic(creature) { }
void JustEngagedWith(Unit* /*who*/) override
{
_events.ScheduleEvent(EVENT_DEATHLY_CHILL, 5s);
_events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 17s);
}
void Reset() override
{
_events.Reset();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_DEATHLY_CHILL:
DoCastVictim(SPELL_DEATHLY_CHILL);
_events.ScheduleEvent(EVENT_DEATHLY_CHILL, 2s + 500ms);
break;
case EVENT_SHADOW_BOLT_VOLLEY:
DoCast(SPELL_SHADOW_BOLT_VOLLEY);
_events.ScheduleEvent(EVENT_SHADOW_BOLT_VOLLEY, 16s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
EventMap _events;
};
enum ShadowBorneChampionEvent
{
EVENT_ANCESTRAL_FURY = 1,
EVENT_VIGILANT_DEFENSE
};
// 134158 - Shadow-Borne Champion
struct npc_kings_rest_shadow_borne_champion : public npc_kings_rest_temple_basic
{
npc_kings_rest_shadow_borne_champion(Creature* creature) : npc_kings_rest_temple_basic(creature) { }
void JustEngagedWith(Unit* /*who*/) override
{
_events.ScheduleEvent(EVENT_ANCESTRAL_FURY, 35s);
_events.ScheduleEvent(EVENT_VIGILANT_DEFENSE, 21s);
}
void Reset() override
{
_events.Reset();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_ANCESTRAL_FURY:
DoCast(SPELL_ANCESTRAL_FURY);
_events.ScheduleEvent(EVENT_ANCESTRAL_FURY, 30s);
break;
case EVENT_VIGILANT_DEFENSE:
DoCast(SPELL_VIGILANT_DEFENSE);
_events.ScheduleEvent(EVENT_VIGILANT_DEFENSE, 21s);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
}
private:
EventMap _events;
};
// 133943 - Minion of Zul
struct npc_kings_rest_minion_of_zul : public npc_kings_rest_temple_basic
{
npc_kings_rest_minion_of_zul(Creature* creature) : npc_kings_rest_temple_basic(creature) { }
void JustAppeared() override
{
if (me->HasStringId("TempleEvent"))
npc_kings_rest_temple_basic::JustAppeared();
DoCast(SPELL_BOUND_BY_SHADOW);
}
void JustEngagedWith(Unit* /*who*/) override
{
DoCast(SPELL_MINION_OF_ZUL_FIXATE);
}
};
// 17933 - Gust Slash - Areatrigger
struct at_kings_rest_gust_slash : AreaTriggerAI
{
at_kings_rest_gust_slash(AreaTrigger* areatrigger) : AreaTriggerAI(areatrigger) { }
void OnUnitEnter(Unit* unit) override
{
if (!unit->IsPlayer())
return;
unit->CastSpell(nullptr, SPELL_GUST_SLASH_DAMAGE, true);
}
};
void AddSC_kings_rest()
{
// Creature
RegisterKingsRestCreatureAI(npc_kings_rest_animated_guardian);
RegisterKingsRestCreatureAI(npc_kings_rest_shadow_of_zul);
RegisterKingsRestCreatureAI(npc_kings_rest_shadow_borne_warrior);
RegisterKingsRestCreatureAI(npc_kings_rest_shadow_borne_witch_doctor);
RegisterKingsRestCreatureAI(npc_kings_rest_shadow_borne_champion);
RegisterKingsRestCreatureAI(npc_kings_rest_minion_of_zul);
// GameObject
RegisterGameObjectAI(go_kings_rest_serpentine_seal);
// Areatrigger
RegisterAreaTriggerAI(at_kings_rest_trigger_intro_event_with_zul);
RegisterAreaTriggerAI(at_kings_rest_gust_slash);
// Conversation
RegisterConversationAI(conversation_kings_rest_intro);
// Spells
RegisterSpellScript(spell_kings_rest_suppression_slam);
RegisterSpellScript(spell_kings_rest_bound_by_shadow);
RegisterSpellScript(spell_kings_rest_pit_of_despair);
}