1178 lines
40 KiB
C++
1178 lines
40 KiB
C++
/*
|
|
* 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 Affero General Public License as published by the
|
|
* Free Software Foundation; either version 3 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 Affero 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* ScriptData
|
|
SDName: Blades_Edge_Mountains
|
|
SD%Complete: 90
|
|
SDComment: Quest support: 10503, 10504, 10556, 10594, 10609, 10821. Ogri'la->Skettis Flight. (npc_daranelle needs bit more work before consider complete)
|
|
SDCategory: Blade's Edge Mountains
|
|
EndScriptData */
|
|
|
|
/* ContentData
|
|
npc_nether_drake
|
|
npc_daranelle
|
|
go_legion_obelisk
|
|
EndContentData */
|
|
|
|
#include "Cell.h"
|
|
#include "CellImpl.h"
|
|
#include "GridNotifiers.h"
|
|
#include "GridNotifiersImpl.h"
|
|
#include "ScriptMgr.h"
|
|
#include "ScriptedCreature.h"
|
|
#include "ScriptedGossip.h"
|
|
#include "SpellAuraEffects.h"
|
|
#include "SpellAuras.h"
|
|
#include "SpellInfo.h"
|
|
#include "SpellScript.h"
|
|
|
|
// Ours
|
|
enum deathsdoorfell
|
|
{
|
|
SPELL_ARTILLERY_ON_THE_WRAP_GATE = 39221,
|
|
SPELL_IMP_AURA = 39227,
|
|
SPELL_HOUND_AURA = 39275,
|
|
SPELL_EXPLOSION = 30934,
|
|
|
|
NPC_DEATHS_DOOR_FEL_CANNON_TARGET_BUNNY = 22495,
|
|
NPC_DEATHS_DOOR_FEL_CANNON = 22443,
|
|
NPC_EXPLOSION_BUNNY = 22502,
|
|
NPC_FEL_IMP = 22474,
|
|
NPC_HOUND = 22500,
|
|
NPC_NORTH_GATE = 22471,
|
|
NPC_SOUTH_GATE = 22472,
|
|
NPC_NORTH_GATE_CREDIT = 22503,
|
|
NPC_SOUTH_GATE_CREDIT = 22504,
|
|
|
|
GO_FIRE = 185317,
|
|
GO_BIG_FIRE = 185319,
|
|
|
|
EVENT_PARTY_TIMER = 1
|
|
};
|
|
|
|
class npc_deaths_door_fell_cannon_target_bunny : public CreatureScript
|
|
{
|
|
public:
|
|
npc_deaths_door_fell_cannon_target_bunny() : CreatureScript("npc_deaths_door_fell_cannon_target_bunny") { }
|
|
|
|
struct npc_deaths_door_fell_cannon_target_bunnyAI : public ScriptedAI
|
|
{
|
|
npc_deaths_door_fell_cannon_target_bunnyAI(Creature* creature) : ScriptedAI(creature), PartyTime(false) { }
|
|
|
|
EventMap events;
|
|
bool PartyTime;
|
|
ObjectGuid PlayerGUID;
|
|
ObjectGuid CannonGUID;
|
|
uint8 count;
|
|
|
|
void Reset() override
|
|
{
|
|
Initialize();
|
|
events.Reset();
|
|
}
|
|
|
|
void Initialize()
|
|
{
|
|
PartyTime = false;
|
|
PlayerGUID.Clear();
|
|
CannonGUID.Clear();
|
|
count = 0;
|
|
}
|
|
|
|
void SpellHit(Unit* caster, SpellInfo const* spell) override
|
|
{
|
|
if (spell->Id == SPELL_ARTILLERY_ON_THE_WRAP_GATE)
|
|
{
|
|
count++;
|
|
|
|
if (count == 1)
|
|
{
|
|
if (Player* player = caster->GetCharmerOrOwnerPlayerOrPlayerItself())
|
|
PlayerGUID = player->GetGUID();
|
|
|
|
CannonGUID = caster->GetGUID();
|
|
PartyTime = true;
|
|
events.ScheduleEvent(EVENT_PARTY_TIMER, 3000);
|
|
}
|
|
|
|
if (count >= 3)
|
|
me->SummonGameObject(GO_FIRE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 130);
|
|
|
|
if (count > 6)
|
|
{
|
|
if (Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID))
|
|
{
|
|
if (GetClosestCreatureWithEntry(me, NPC_SOUTH_GATE, 200.0f))
|
|
player->KilledMonsterCredit(NPC_SOUTH_GATE_CREDIT);
|
|
else if (GetClosestCreatureWithEntry(me, NPC_NORTH_GATE, 200.0f))
|
|
player->KilledMonsterCredit(NPC_NORTH_GATE_CREDIT);
|
|
// complete quest part
|
|
if (Creature* bunny = GetClosestCreatureWithEntry(me, NPC_EXPLOSION_BUNNY, 200.0f))
|
|
bunny->CastSpell(nullptr, SPELL_EXPLOSION, TRIGGERED_NONE);
|
|
if (Creature* cannon = ObjectAccessor::GetCreature(*me, CannonGUID))
|
|
cannon->DespawnOrUnsummon(5000);
|
|
}
|
|
|
|
me->SummonGameObject(GO_BIG_FIRE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 60);
|
|
Reset();
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void JustSummoned(Creature* summoned) override
|
|
{
|
|
if (summoned->GetEntry() == NPC_FEL_IMP)
|
|
summoned->CastSpell(summoned, SPELL_IMP_AURA, true);
|
|
else if (summoned->GetEntry() == NPC_HOUND)
|
|
summoned->CastSpell(summoned, SPELL_HOUND_AURA, true);
|
|
|
|
if (Creature* Target = GetClosestCreatureWithEntry(me, NPC_DEATHS_DOOR_FEL_CANNON, 200.0f))
|
|
{
|
|
Target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_ATTACKABLE_1); // attack the cannon
|
|
summoned->AI()->AttackStart(Target);
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
events.Update(diff);
|
|
|
|
if (PartyTime)
|
|
{
|
|
if (Creature* cannon = ObjectAccessor::GetCreature(*me, CannonGUID))
|
|
{
|
|
if (!cannon || !cannon->GetCharmerOrOwnerGUID())
|
|
Reset();
|
|
}
|
|
|
|
switch (events.ExecuteEvent())
|
|
{
|
|
case EVENT_PARTY_TIMER:
|
|
if (roll_chance_i(20))
|
|
me->SummonCreature(NPC_HOUND, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
|
|
else
|
|
me->SummonCreature(NPC_FEL_IMP, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
|
|
events.ScheduleEvent(EVENT_PARTY_TIMER, 3000);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const override
|
|
{
|
|
return new npc_deaths_door_fell_cannon_target_bunnyAI(creature);
|
|
}
|
|
};
|
|
|
|
class npc_deaths_fel_cannon : public CreatureScript
|
|
{
|
|
public:
|
|
npc_deaths_fel_cannon() : CreatureScript("npc_deaths_fel_cannon") { }
|
|
|
|
struct npc_deaths_fel_cannonAI : public ScriptedAI
|
|
{
|
|
npc_deaths_fel_cannonAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void Reset() override
|
|
{
|
|
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1);
|
|
}
|
|
|
|
void UpdateAI(uint32 /*diff*/) override
|
|
{
|
|
if (me->IsNonMeleeSpellCast(false))
|
|
return;
|
|
|
|
if (Creature* Target = GetClosestCreatureWithEntry(me, NPC_DEATHS_DOOR_FEL_CANNON_TARGET_BUNNY, 200.0f))
|
|
{
|
|
me->SetFacingToObject(Target);
|
|
me->TauntFadeOut(Target);
|
|
me->CombatStop(); // force
|
|
}
|
|
|
|
Reset();
|
|
}
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const override
|
|
{
|
|
return new npc_deaths_fel_cannonAI(creature);
|
|
}
|
|
};
|
|
|
|
class spell_npc22275_crystal_prison : public SpellScriptLoader
|
|
{
|
|
public:
|
|
spell_npc22275_crystal_prison() : SpellScriptLoader("spell_npc22275_crystal_prison") { }
|
|
|
|
class spell_npc22275_crystal_prison_AuraScript : public AuraScript
|
|
{
|
|
PrepareAuraScript(spell_npc22275_crystal_prison_AuraScript);
|
|
|
|
void OnPeriodic(AuraEffect const* /*aurEff*/)
|
|
{
|
|
PreventDefaultAction();
|
|
SetDuration(0);
|
|
GetTarget()->CastSpell(GetTarget(), 40898, true);
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnEffectPeriodic += AuraEffectPeriodicFn(spell_npc22275_crystal_prison_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
|
|
}
|
|
};
|
|
|
|
AuraScript* GetAuraScript() const override
|
|
{
|
|
return new spell_npc22275_crystal_prison_AuraScript();
|
|
}
|
|
};
|
|
|
|
// Theirs
|
|
|
|
/*######
|
|
## npc_nether_drake
|
|
######*/
|
|
|
|
enum Netherdrake
|
|
{
|
|
//Used by 20021, 21817, 21820, 21821, 21823 but not existing in database
|
|
SAY_NIHIL_1 = 0,
|
|
SAY_NIHIL_2 = 1,
|
|
SAY_NIHIL_3 = 2,
|
|
SAY_NIHIL_4 = 3,
|
|
SAY_NIHIL_INTERRUPT = 4,
|
|
|
|
ENTRY_WHELP = 20021,
|
|
ENTRY_PROTO = 21821,
|
|
ENTRY_ADOLE = 21817,
|
|
ENTRY_MATUR = 21820,
|
|
ENTRY_NIHIL = 21823,
|
|
|
|
SPELL_T_PHASE_MODULATOR = 37573,
|
|
|
|
SPELL_ARCANE_BLAST = 38881,
|
|
SPELL_MANA_BURN = 38884,
|
|
SPELL_INTANGIBLE_PRESENCE = 36513
|
|
};
|
|
|
|
class npc_nether_drake : public CreatureScript
|
|
{
|
|
public:
|
|
npc_nether_drake() : CreatureScript("npc_nether_drake") { }
|
|
|
|
struct npc_nether_drakeAI : public ScriptedAI
|
|
{
|
|
npc_nether_drakeAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
bool IsNihil;
|
|
uint32 NihilSpeech_Timer;
|
|
uint32 NihilSpeech_Phase;
|
|
|
|
uint32 ArcaneBlast_Timer;
|
|
uint32 ManaBurn_Timer;
|
|
uint32 IntangiblePresence_Timer;
|
|
|
|
void Reset() override
|
|
{
|
|
IsNihil = false;
|
|
NihilSpeech_Timer = 3000;
|
|
NihilSpeech_Phase = 0;
|
|
|
|
ArcaneBlast_Timer = 7500;
|
|
ManaBurn_Timer = 10000;
|
|
IntangiblePresence_Timer = 15000;
|
|
}
|
|
|
|
void EnterCombat(Unit* /*who*/) override { }
|
|
|
|
void MoveInLineOfSight(Unit* who) override
|
|
|
|
{
|
|
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
|
|
return;
|
|
|
|
ScriptedAI::MoveInLineOfSight(who);
|
|
}
|
|
|
|
//in case Creature was not summoned (not expected)
|
|
void MovementInform(uint32 type, uint32 id) override
|
|
{
|
|
if (type != POINT_MOTION_TYPE)
|
|
return;
|
|
|
|
if (id == 0)
|
|
{
|
|
me->setDeathState(JUST_DIED);
|
|
me->RemoveCorpse();
|
|
me->SetHealth(0);
|
|
}
|
|
}
|
|
|
|
void SpellHit(Unit* caster, const SpellInfo* spell) override
|
|
{
|
|
if (spell->Id == SPELL_T_PHASE_MODULATOR && caster->GetTypeId() == TYPEID_PLAYER)
|
|
{
|
|
const uint32 entry_list[4] = {ENTRY_PROTO, ENTRY_ADOLE, ENTRY_MATUR, ENTRY_NIHIL};
|
|
int cid = rand() % (4 - 1);
|
|
|
|
if (entry_list[cid] == me->GetEntry())
|
|
++cid;
|
|
|
|
//we are nihil, so say before transform
|
|
if (me->GetEntry() == ENTRY_NIHIL)
|
|
{
|
|
Talk(SAY_NIHIL_INTERRUPT);
|
|
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
|
|
IsNihil = false;
|
|
}
|
|
|
|
if (me->UpdateEntry(entry_list[cid]))
|
|
{
|
|
if (entry_list[cid] == ENTRY_NIHIL)
|
|
{
|
|
EnterEvadeMode();
|
|
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
|
|
IsNihil = true;
|
|
}
|
|
else
|
|
AttackStart(caster);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
if (IsNihil)
|
|
{
|
|
if (NihilSpeech_Timer <= diff)
|
|
{
|
|
switch (NihilSpeech_Phase)
|
|
{
|
|
case 0:
|
|
Talk(SAY_NIHIL_1);
|
|
++NihilSpeech_Phase;
|
|
break;
|
|
case 1:
|
|
Talk(SAY_NIHIL_2);
|
|
++NihilSpeech_Phase;
|
|
break;
|
|
case 2:
|
|
Talk(SAY_NIHIL_3);
|
|
++NihilSpeech_Phase;
|
|
break;
|
|
case 3:
|
|
Talk(SAY_NIHIL_4);
|
|
++NihilSpeech_Phase;
|
|
break;
|
|
case 4:
|
|
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
|
|
//take off to location above
|
|
me->GetMotionMaster()->MovePoint(0, me->GetPositionX() + 50.0f, me->GetPositionY(), me->GetPositionZ() + 50.0f);
|
|
++NihilSpeech_Phase;
|
|
break;
|
|
}
|
|
NihilSpeech_Timer = 5000;
|
|
}
|
|
else NihilSpeech_Timer -= diff;
|
|
|
|
//anything below here is not interesting for Nihil, so skip it
|
|
return;
|
|
}
|
|
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
if (IntangiblePresence_Timer <= diff)
|
|
{
|
|
DoCastVictim(SPELL_INTANGIBLE_PRESENCE);
|
|
IntangiblePresence_Timer = 15000 + rand() % 15000;
|
|
}
|
|
else IntangiblePresence_Timer -= diff;
|
|
|
|
if (ManaBurn_Timer <= diff)
|
|
{
|
|
Unit* target = me->GetVictim();
|
|
if (target && target->getPowerType() == POWER_MANA)
|
|
DoCast(target, SPELL_MANA_BURN);
|
|
ManaBurn_Timer = 8000 + rand() % 8000;
|
|
}
|
|
else ManaBurn_Timer -= diff;
|
|
|
|
if (ArcaneBlast_Timer <= diff)
|
|
{
|
|
DoCastVictim(SPELL_ARCANE_BLAST);
|
|
ArcaneBlast_Timer = 2500 + rand() % 5000;
|
|
}
|
|
else ArcaneBlast_Timer -= diff;
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const override
|
|
{
|
|
return new npc_nether_drakeAI(creature);
|
|
}
|
|
};
|
|
|
|
/*######
|
|
## npc_daranelle
|
|
######*/
|
|
|
|
enum Daranelle
|
|
{
|
|
SAY_SPELL_INFLUENCE = 0,
|
|
SPELL_LASHHAN_CHANNEL = 36904,
|
|
SPELL_DISPELLING_ANALYSIS = 37028,
|
|
|
|
NPC_KALIRI_TOTEM = 21468
|
|
};
|
|
|
|
class npc_daranelle : public CreatureScript
|
|
{
|
|
public:
|
|
npc_daranelle() : CreatureScript("npc_daranelle") { }
|
|
|
|
struct npc_daranelleAI : public ScriptedAI
|
|
{
|
|
npc_daranelleAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void Reset() override { }
|
|
|
|
void EnterCombat(Unit* /*who*/) override { }
|
|
|
|
void MoveInLineOfSight(Unit* who) override
|
|
|
|
{
|
|
if (who->GetTypeId() == TYPEID_PLAYER)
|
|
{
|
|
if (who->HasAura(SPELL_LASHHAN_CHANNEL) && me->IsWithinDistInMap(who, 10.0f))
|
|
{
|
|
if (Creature* bird = who->FindNearestCreature(NPC_KALIRI_TOTEM, 10.0f))
|
|
{
|
|
Talk(SAY_SPELL_INFLUENCE, who);
|
|
/// @todo Move the below to updateAI and run if this statement == true
|
|
DoCast(who, SPELL_DISPELLING_ANALYSIS, true);
|
|
bird->DespawnOrUnsummon(2000);
|
|
}
|
|
}
|
|
}
|
|
|
|
ScriptedAI::MoveInLineOfSight(who);
|
|
}
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const override
|
|
{
|
|
return new npc_daranelleAI(creature);
|
|
}
|
|
};
|
|
|
|
enum SimonGame
|
|
{
|
|
NPC_SIMON_BUNNY = 22923,
|
|
NPC_APEXIS_GUARDIAN = 22275,
|
|
|
|
GO_APEXIS_RELIC = 185890,
|
|
GO_APEXIS_MONUMENT = 185944,
|
|
GO_AURA_BLUE = 185872,
|
|
GO_AURA_GREEN = 185873,
|
|
GO_AURA_RED = 185874,
|
|
GO_AURA_YELLOW = 185875,
|
|
|
|
GO_BLUE_CLUSTER_DISPLAY = 7369,
|
|
GO_GREEN_CLUSTER_DISPLAY = 7371,
|
|
GO_RED_CLUSTER_DISPLAY = 7373,
|
|
GO_YELLOW_CLUSTER_DISPLAY = 7375,
|
|
GO_BLUE_CLUSTER_DISPLAY_LARGE = 7364,
|
|
GO_GREEN_CLUSTER_DISPLAY_LARGE = 7365,
|
|
GO_RED_CLUSTER_DISPLAY_LARGE = 7366,
|
|
GO_YELLOW_CLUSTER_DISPLAY_LARGE = 7367,
|
|
|
|
SPELL_PRE_GAME_BLUE = 40176,
|
|
SPELL_PRE_GAME_GREEN = 40177,
|
|
SPELL_PRE_GAME_RED = 40178,
|
|
SPELL_PRE_GAME_YELLOW = 40179,
|
|
SPELL_VISUAL_BLUE = 40244,
|
|
SPELL_VISUAL_GREEN = 40245,
|
|
SPELL_VISUAL_RED = 40246,
|
|
SPELL_VISUAL_YELLOW = 40247,
|
|
|
|
SOUND_BLUE = 11588,
|
|
SOUND_GREEN = 11589,
|
|
SOUND_RED = 11590,
|
|
SOUND_YELLOW = 11591,
|
|
SOUND_DISABLE_NODE = 11758,
|
|
|
|
SPELL_AUDIBLE_GAME_TICK = 40391,
|
|
SPELL_VISUAL_START_PLAYER_LEVEL = 40436,
|
|
SPELL_VISUAL_START_AI_LEVEL = 40387,
|
|
|
|
SPELL_BAD_PRESS_TRIGGER = 41241,
|
|
SPELL_BAD_PRESS_DAMAGE = 40065,
|
|
SPELL_REWARD_BUFF_1 = 40310,
|
|
SPELL_REWARD_BUFF_2 = 40311,
|
|
SPELL_REWARD_BUFF_3 = 40312,
|
|
};
|
|
|
|
enum SimonEvents
|
|
{
|
|
EVENT_SIMON_SETUP_PRE_GAME = 1,
|
|
EVENT_SIMON_PLAY_SEQUENCE = 2,
|
|
EVENT_SIMON_RESET_CLUSTERS = 3,
|
|
EVENT_SIMON_PERIODIC_PLAYER_CHECK = 4,
|
|
EVENT_SIMON_TOO_LONG_TIME = 5,
|
|
EVENT_SIMON_GAME_TICK = 6,
|
|
EVENT_SIMON_ROUND_FINISHED = 7,
|
|
|
|
ACTION_SIMON_CORRECT_FULL_SEQUENCE = 8,
|
|
ACTION_SIMON_WRONG_SEQUENCE = 9,
|
|
ACTION_SIMON_ROUND_FINISHED = 10,
|
|
};
|
|
|
|
enum SimonColors
|
|
{
|
|
SIMON_BLUE = 0,
|
|
SIMON_RED = 1,
|
|
SIMON_GREEN = 2,
|
|
SIMON_YELLOW = 3,
|
|
SIMON_MAX_COLORS = 4,
|
|
};
|
|
|
|
class npc_simon_bunny : public CreatureScript
|
|
{
|
|
public:
|
|
npc_simon_bunny() : CreatureScript("npc_simon_bunny") { }
|
|
|
|
struct npc_simon_bunnyAI : public ScriptedAI
|
|
{
|
|
npc_simon_bunnyAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
bool large;
|
|
bool listening;
|
|
uint8 gameLevel;
|
|
uint8 fails;
|
|
uint8 gameTicks;
|
|
ObjectGuid playerGUID;
|
|
uint32 clusterIds[SIMON_MAX_COLORS];
|
|
float zCoordCorrection;
|
|
float searchDistance;
|
|
EventMap _events;
|
|
std::list<uint8> colorSequence, playableSequence, playerSequence;
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
|
|
switch (_events.ExecuteEvent())
|
|
{
|
|
case EVENT_SIMON_PERIODIC_PLAYER_CHECK:
|
|
if (!CheckPlayer())
|
|
ResetNode();
|
|
else
|
|
_events.ScheduleEvent(EVENT_SIMON_PERIODIC_PLAYER_CHECK, 2000);
|
|
break;
|
|
case EVENT_SIMON_SETUP_PRE_GAME:
|
|
SetUpPreGame();
|
|
_events.CancelEvent(EVENT_SIMON_GAME_TICK);
|
|
_events.ScheduleEvent(EVENT_SIMON_PLAY_SEQUENCE, 1000);
|
|
break;
|
|
case EVENT_SIMON_PLAY_SEQUENCE:
|
|
if (!playableSequence.empty())
|
|
{
|
|
PlayNextColor();
|
|
_events.ScheduleEvent(EVENT_SIMON_PLAY_SEQUENCE, 1500);
|
|
}
|
|
else
|
|
{
|
|
listening = true;
|
|
DoCast(SPELL_VISUAL_START_PLAYER_LEVEL);
|
|
playerSequence.clear();
|
|
PrepareClusters();
|
|
gameTicks = 0;
|
|
_events.ScheduleEvent(EVENT_SIMON_GAME_TICK, 3000);
|
|
}
|
|
break;
|
|
case EVENT_SIMON_GAME_TICK:
|
|
DoCast(SPELL_AUDIBLE_GAME_TICK);
|
|
|
|
if (gameTicks > gameLevel)
|
|
_events.ScheduleEvent(EVENT_SIMON_TOO_LONG_TIME, 500);
|
|
else
|
|
_events.ScheduleEvent(EVENT_SIMON_GAME_TICK, 3000);
|
|
gameTicks++;
|
|
break;
|
|
case EVENT_SIMON_RESET_CLUSTERS:
|
|
PrepareClusters(true);
|
|
break;
|
|
case EVENT_SIMON_TOO_LONG_TIME:
|
|
DoAction(ACTION_SIMON_WRONG_SEQUENCE);
|
|
break;
|
|
case EVENT_SIMON_ROUND_FINISHED:
|
|
DoAction(ACTION_SIMON_ROUND_FINISHED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DoAction(int32 action) override
|
|
{
|
|
switch (action)
|
|
{
|
|
case ACTION_SIMON_ROUND_FINISHED:
|
|
listening = false;
|
|
DoCast(SPELL_VISUAL_START_AI_LEVEL);
|
|
GiveRewardForLevel(gameLevel);
|
|
_events.CancelEventGroup(0);
|
|
if (gameLevel == 10)
|
|
ResetNode();
|
|
else
|
|
_events.ScheduleEvent(EVENT_SIMON_SETUP_PRE_GAME, 1000);
|
|
break;
|
|
case ACTION_SIMON_CORRECT_FULL_SEQUENCE:
|
|
gameLevel++;
|
|
DoAction(ACTION_SIMON_ROUND_FINISHED);
|
|
break;
|
|
case ACTION_SIMON_WRONG_SEQUENCE:
|
|
GivePunishment();
|
|
DoAction(ACTION_SIMON_ROUND_FINISHED);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Called by color clusters script (go_simon_cluster) and used for knowing the button pressed by player
|
|
void SetData(uint32 type, uint32 /*data*/) override
|
|
{
|
|
if (!listening)
|
|
return;
|
|
|
|
uint8 pressedColor = SIMON_MAX_COLORS;
|
|
|
|
if (type == clusterIds[SIMON_RED])
|
|
pressedColor = SIMON_RED;
|
|
else if (type == clusterIds[SIMON_BLUE])
|
|
pressedColor = SIMON_BLUE;
|
|
else if (type == clusterIds[SIMON_GREEN])
|
|
pressedColor = SIMON_GREEN;
|
|
else if (type == clusterIds[SIMON_YELLOW])
|
|
pressedColor = SIMON_YELLOW;
|
|
|
|
PlayColor(pressedColor);
|
|
playerSequence.push_back(pressedColor);
|
|
_events.ScheduleEvent(EVENT_SIMON_RESET_CLUSTERS, 500);
|
|
CheckPlayerSequence();
|
|
}
|
|
|
|
// Used for getting involved player guid. Parameter id is used for defining if is a large(Monument) or small(Relic) node
|
|
void SetGUID(ObjectGuid guid, int32 id) override
|
|
{
|
|
me->SetCanFly(true);
|
|
|
|
large = (bool)id;
|
|
playerGUID = guid;
|
|
StartGame();
|
|
}
|
|
|
|
/*
|
|
Resets all variables and also find the ids of the four closests color clusters, since every simon
|
|
node have diferent ids for clusters this is absolutely NECESSARY.
|
|
*/
|
|
void StartGame()
|
|
{
|
|
listening = false;
|
|
gameLevel = 0;
|
|
fails = 0;
|
|
gameTicks = 0;
|
|
zCoordCorrection = large ? 8.0f : 2.75f;
|
|
searchDistance = large ? 13.0f : 5.0f;
|
|
colorSequence.clear();
|
|
playableSequence.clear();
|
|
playerSequence.clear();
|
|
me->SetObjectScale(large ? 2.0f : 1.0f);
|
|
|
|
std::list<WorldObject*> ClusterList;
|
|
Acore::AllWorldObjectsInRange objects(me, searchDistance);
|
|
Acore::WorldObjectListSearcher<Acore::AllWorldObjectsInRange> searcher(me, ClusterList, objects);
|
|
Cell::VisitAllObjects(me, searcher, searchDistance);
|
|
|
|
for (std::list<WorldObject*>::const_iterator i = ClusterList.begin(); i != ClusterList.end(); ++i)
|
|
{
|
|
if (GameObject* go = (*i)->ToGameObject())
|
|
{
|
|
// We are checking for displayid because all simon nodes have 4 clusters with different entries
|
|
if (large)
|
|
{
|
|
switch (go->GetGOInfo()->displayId)
|
|
{
|
|
case GO_BLUE_CLUSTER_DISPLAY_LARGE:
|
|
clusterIds[SIMON_BLUE] = go->GetEntry();
|
|
break;
|
|
|
|
case GO_RED_CLUSTER_DISPLAY_LARGE:
|
|
clusterIds[SIMON_RED] = go->GetEntry();
|
|
break;
|
|
|
|
case GO_GREEN_CLUSTER_DISPLAY_LARGE:
|
|
clusterIds[SIMON_GREEN] = go->GetEntry();
|
|
break;
|
|
|
|
case GO_YELLOW_CLUSTER_DISPLAY_LARGE:
|
|
clusterIds[SIMON_YELLOW] = go->GetEntry();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (go->GetGOInfo()->displayId)
|
|
{
|
|
case GO_BLUE_CLUSTER_DISPLAY:
|
|
clusterIds[SIMON_BLUE] = go->GetEntry();
|
|
break;
|
|
|
|
case GO_RED_CLUSTER_DISPLAY:
|
|
clusterIds[SIMON_RED] = go->GetEntry();
|
|
break;
|
|
|
|
case GO_GREEN_CLUSTER_DISPLAY:
|
|
clusterIds[SIMON_GREEN] = go->GetEntry();
|
|
break;
|
|
|
|
case GO_YELLOW_CLUSTER_DISPLAY:
|
|
clusterIds[SIMON_YELLOW] = go->GetEntry();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_events.Reset();
|
|
_events.ScheduleEvent(EVENT_SIMON_ROUND_FINISHED, 1000);
|
|
_events.ScheduleEvent(EVENT_SIMON_PERIODIC_PLAYER_CHECK, 2000);
|
|
|
|
if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance))
|
|
relic->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
|
|
}
|
|
|
|
// Called when despawning the bunny. Sets all the node GOs to their default states.
|
|
void ResetNode()
|
|
{
|
|
DoPlaySoundToSet(me, SOUND_DISABLE_NODE);
|
|
|
|
for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++)
|
|
if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], searchDistance))
|
|
cluster->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
|
|
|
|
for (uint32 auraId = GO_AURA_BLUE; auraId <= GO_AURA_YELLOW; auraId++)
|
|
if (GameObject* auraGo = me->FindNearestGameObject(auraId, searchDistance))
|
|
auraGo->RemoveFromWorld();
|
|
|
|
if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance))
|
|
relic->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
|
|
|
|
me->DespawnOrUnsummon(1000);
|
|
}
|
|
|
|
/*
|
|
Called on every button click of player. Adds the clicked color to the player created sequence and
|
|
checks if it corresponds to the AI created sequence. If so, incremente gameLevel and start a new
|
|
round, if not, give punishment and restart current level.
|
|
*/
|
|
void CheckPlayerSequence()
|
|
{
|
|
bool correct = true;
|
|
if (playerSequence.size() <= colorSequence.size())
|
|
for (std::list<uint8>::const_iterator i = playerSequence.begin(), j = colorSequence.begin(); i != playerSequence.end(); ++i, ++j)
|
|
if ((*i) != (*j))
|
|
correct = false;
|
|
|
|
if (correct && (playerSequence.size() == colorSequence.size()))
|
|
DoAction(ACTION_SIMON_CORRECT_FULL_SEQUENCE);
|
|
else if (!correct)
|
|
DoAction(ACTION_SIMON_WRONG_SEQUENCE);
|
|
}
|
|
|
|
/*
|
|
Generates a random sequence of colors depending on the gameLevel. We also copy this sequence to
|
|
the playableSequence wich will be used when playing the sequence to the player.
|
|
*/
|
|
void GenerateColorSequence()
|
|
{
|
|
colorSequence.clear();
|
|
for (uint8 i = 0; i <= gameLevel; i++)
|
|
colorSequence.push_back(RAND(SIMON_BLUE, SIMON_RED, SIMON_GREEN, SIMON_YELLOW));
|
|
|
|
for (std::list<uint8>::const_iterator i = colorSequence.begin(); i != colorSequence.end(); ++i)
|
|
playableSequence.push_back(*i);
|
|
}
|
|
|
|
// Remove any existant glowing auras over clusters and set clusters ready for interating with them.
|
|
void PrepareClusters(bool clustersOnly = false)
|
|
{
|
|
for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++)
|
|
if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], searchDistance))
|
|
cluster->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
|
|
|
|
if (clustersOnly)
|
|
return;
|
|
|
|
for (uint32 auraId = GO_AURA_BLUE; auraId <= GO_AURA_YELLOW; auraId++)
|
|
if (GameObject* auraGo = me->FindNearestGameObject(auraId, searchDistance))
|
|
auraGo->RemoveFromWorld();
|
|
}
|
|
|
|
/*
|
|
Called when AI is playing the sequence for player. We cast the visual spell and then remove the
|
|
cast color from the casting sequence.
|
|
*/
|
|
void PlayNextColor()
|
|
{
|
|
PlayColor(*playableSequence.begin());
|
|
playableSequence.erase(playableSequence.begin());
|
|
}
|
|
|
|
// Casts a spell and plays a sound depending on parameter color.
|
|
void PlayColor(uint8 color)
|
|
{
|
|
switch (color)
|
|
{
|
|
case SIMON_BLUE:
|
|
DoCast(SPELL_VISUAL_BLUE);
|
|
DoPlaySoundToSet(me, SOUND_BLUE);
|
|
break;
|
|
case SIMON_GREEN:
|
|
DoCast(SPELL_VISUAL_GREEN);
|
|
DoPlaySoundToSet(me, SOUND_GREEN);
|
|
break;
|
|
case SIMON_RED:
|
|
DoCast(SPELL_VISUAL_RED);
|
|
DoPlaySoundToSet(me, SOUND_RED);
|
|
break;
|
|
case SIMON_YELLOW:
|
|
DoCast(SPELL_VISUAL_YELLOW);
|
|
DoPlaySoundToSet(me, SOUND_YELLOW);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Creates the transparent glowing auras on every cluster of this node.
|
|
After calling this function bunny is teleported to the center of the node.
|
|
*/
|
|
void SetUpPreGame()
|
|
{
|
|
for (uint32 clusterId = SIMON_BLUE; clusterId < SIMON_MAX_COLORS; clusterId++)
|
|
{
|
|
if (GameObject* cluster = me->FindNearestGameObject(clusterIds[clusterId], 2.0f * searchDistance))
|
|
{
|
|
cluster->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
|
|
|
|
// break since we don't need glowing auras for large clusters
|
|
if (large)
|
|
break;
|
|
|
|
float x, y, z, o;
|
|
cluster->GetPosition(x, y, z, o);
|
|
me->NearTeleportTo(x, y, z, o);
|
|
|
|
uint32 preGameSpellId;
|
|
if (cluster->GetEntry() == clusterIds[SIMON_RED])
|
|
preGameSpellId = SPELL_PRE_GAME_RED;
|
|
else if (cluster->GetEntry() == clusterIds[SIMON_BLUE])
|
|
preGameSpellId = SPELL_PRE_GAME_BLUE;
|
|
else if (cluster->GetEntry() == clusterIds[SIMON_GREEN])
|
|
preGameSpellId = SPELL_PRE_GAME_GREEN;
|
|
else if (cluster->GetEntry() == clusterIds[SIMON_YELLOW])
|
|
preGameSpellId = SPELL_PRE_GAME_YELLOW;
|
|
else break;
|
|
|
|
me->CastSpell(cluster, preGameSpellId, true);
|
|
}
|
|
}
|
|
|
|
if (GameObject* relic = me->FindNearestGameObject(large ? GO_APEXIS_MONUMENT : GO_APEXIS_RELIC, searchDistance))
|
|
{
|
|
float x, y, z, o;
|
|
relic->GetPosition(x, y, z, o);
|
|
me->NearTeleportTo(x, y, z + zCoordCorrection, o);
|
|
}
|
|
|
|
GenerateColorSequence();
|
|
}
|
|
|
|
// Handles the spell rewards. The spells also have the QuestCompleteEffect, so quests credits are working.
|
|
void GiveRewardForLevel(uint8 level)
|
|
{
|
|
uint32 rewSpell = 0;
|
|
switch (level)
|
|
{
|
|
case 6:
|
|
if (large)
|
|
GivePunishment();
|
|
else
|
|
rewSpell = SPELL_REWARD_BUFF_1;
|
|
break;
|
|
case 8:
|
|
rewSpell = SPELL_REWARD_BUFF_2;
|
|
break;
|
|
case 10:
|
|
rewSpell = SPELL_REWARD_BUFF_3;
|
|
break;
|
|
}
|
|
|
|
if (rewSpell)
|
|
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
|
|
DoCast(player, rewSpell, true);
|
|
}
|
|
|
|
/*
|
|
Depending on the number of failed pushes for player the damage of the spell scales, so we first
|
|
cast the spell on the target that hits for 50 and shows the visual and then forces the player
|
|
to cast the damaging spell on it self with the modified basepoints.
|
|
4 fails = death.
|
|
On large nodes punishment and reward are the same, summoning the Apexis Guardian.
|
|
*/
|
|
void GivePunishment()
|
|
{
|
|
if (large)
|
|
{
|
|
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
|
|
if (Creature* guardian = me->SummonCreature(NPC_APEXIS_GUARDIAN, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() - zCoordCorrection, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20000))
|
|
guardian->AI()->AttackStart(player);
|
|
|
|
ResetNode();
|
|
}
|
|
else
|
|
{
|
|
fails++;
|
|
|
|
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
|
|
DoCast(player, SPELL_BAD_PRESS_TRIGGER, true);
|
|
|
|
if (fails >= 4)
|
|
ResetNode();
|
|
}
|
|
}
|
|
|
|
void SpellHitTarget(Unit* target, const SpellInfo* spell) override
|
|
{
|
|
// Cast SPELL_BAD_PRESS_DAMAGE with scaled basepoints when the visual hits the target.
|
|
// Need Fix: When SPELL_BAD_PRESS_TRIGGER hits target it triggers spell SPELL_BAD_PRESS_DAMAGE by itself
|
|
// so player gets damage equal to calculated damage dbc basepoints for SPELL_BAD_PRESS_DAMAGE (~50)
|
|
if (spell->Id == SPELL_BAD_PRESS_TRIGGER)
|
|
{
|
|
int32 bp = (int32)((float)(fails) * 0.33f * target->GetMaxHealth());
|
|
target->CastCustomSpell(target, SPELL_BAD_PRESS_DAMAGE, &bp, nullptr, nullptr, true);
|
|
}
|
|
}
|
|
|
|
// Checks if player has already die or has get too far from the current node
|
|
bool CheckPlayer()
|
|
{
|
|
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID))
|
|
{
|
|
if (player->isDead())
|
|
return false;
|
|
if (player->GetDistance2d(me) >= 2.0f * searchDistance)
|
|
{
|
|
GivePunishment();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const override
|
|
{
|
|
return new npc_simon_bunnyAI(creature);
|
|
}
|
|
};
|
|
|
|
class go_simon_cluster : public GameObjectScript
|
|
{
|
|
public:
|
|
go_simon_cluster() : GameObjectScript("go_simon_cluster") { }
|
|
|
|
bool OnGossipHello(Player* player, GameObject* go) override
|
|
{
|
|
if (Creature* bunny = go->FindNearestCreature(NPC_SIMON_BUNNY, 12.0f, true))
|
|
bunny->AI()->SetData(go->GetEntry(), 0);
|
|
|
|
player->CastSpell(player, go->GetGOInfo()->goober.spellId, true);
|
|
go->AddUse();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
enum ApexisRelic
|
|
{
|
|
QUEST_CRYSTALS = 11025,
|
|
GOSSIP_TEXT_ID = 10948,
|
|
|
|
ITEM_APEXIS_SHARD = 32569,
|
|
SPELL_TAKE_REAGENTS_SOLO = 41145,
|
|
SPELL_TAKE_REAGENTS_GROUP = 41146,
|
|
};
|
|
|
|
class go_apexis_relic : public GameObjectScript
|
|
{
|
|
public:
|
|
go_apexis_relic() : GameObjectScript("go_apexis_relic") { }
|
|
|
|
bool OnGossipHello(Player* player, GameObject* go) override
|
|
{
|
|
player->PrepareGossipMenu(go, go->GetGOInfo()->questgiver.gossipID);
|
|
player->SendPreparedGossip(go);
|
|
return true;
|
|
}
|
|
|
|
bool OnGossipSelect(Player* player, GameObject* go, uint32 /*sender*/, uint32 /*action*/) override
|
|
{
|
|
CloseGossipMenuFor(player);
|
|
|
|
bool large = (go->GetEntry() == GO_APEXIS_MONUMENT);
|
|
if (player->HasItemCount(ITEM_APEXIS_SHARD, large ? 35 : 1))
|
|
{
|
|
player->CastSpell(player, large ? SPELL_TAKE_REAGENTS_GROUP : SPELL_TAKE_REAGENTS_SOLO, false);
|
|
|
|
if (Creature* bunny = player->SummonCreature(NPC_SIMON_BUNNY, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ()))
|
|
bunny->AI()->SetGUID(player->GetGUID(), large);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
/*######
|
|
## npc_oscillating_frequency_scanner_master_bunny used for quest 10594 "Gauging the Resonant Frequency"
|
|
######*/
|
|
|
|
enum ScannerMasterBunny
|
|
{
|
|
NPC_OSCILLATING_FREQUENCY_SCANNER_TOP_BUNNY = 21759,
|
|
GO_OSCILLATING_FREQUENCY_SCANNER = 184926,
|
|
SPELL_OSCILLATION_FIELD = 37408,
|
|
QUEST_GAUGING_THE_RESONANT_FREQUENCY = 10594
|
|
};
|
|
|
|
class npc_oscillating_frequency_scanner_master_bunny : public CreatureScript
|
|
{
|
|
public:
|
|
npc_oscillating_frequency_scanner_master_bunny() : CreatureScript("npc_oscillating_frequency_scanner_master_bunny") { }
|
|
|
|
struct npc_oscillating_frequency_scanner_master_bunnyAI : public ScriptedAI
|
|
{
|
|
npc_oscillating_frequency_scanner_master_bunnyAI(Creature* creature) : ScriptedAI(creature)
|
|
{
|
|
playerGuid.Clear();
|
|
timer = 500;
|
|
}
|
|
|
|
void Reset() override
|
|
{
|
|
if (GetClosestCreatureWithEntry(me, NPC_OSCILLATING_FREQUENCY_SCANNER_TOP_BUNNY, 25.0f))
|
|
me->DespawnOrUnsummon();
|
|
else
|
|
{
|
|
// Spell 37392 does not exist in dbc, manually spawning
|
|
me->SummonCreature(NPC_OSCILLATING_FREQUENCY_SCANNER_TOP_BUNNY, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 50000);
|
|
me->SummonGameObject(GO_OSCILLATING_FREQUENCY_SCANNER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), 0, 0, 0, 0, 50);
|
|
me->DespawnOrUnsummon(50000);
|
|
}
|
|
|
|
timer = 500;
|
|
}
|
|
|
|
void IsSummonedBy(Unit* summoner) override
|
|
{
|
|
if (summoner && summoner->isType(TYPEMASK_PLAYER))
|
|
playerGuid = summoner->GetGUID();
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
if (timer <= diff)
|
|
{
|
|
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGuid))
|
|
DoCast(player, SPELL_OSCILLATION_FIELD);
|
|
|
|
timer = 3000;
|
|
}
|
|
else
|
|
timer -= diff;
|
|
}
|
|
|
|
private:
|
|
ObjectGuid playerGuid;
|
|
uint32 timer;
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const override
|
|
{
|
|
return new npc_oscillating_frequency_scanner_master_bunnyAI(creature);
|
|
}
|
|
};
|
|
|
|
class spell_oscillating_field : public SpellScriptLoader
|
|
{
|
|
public:
|
|
spell_oscillating_field() : SpellScriptLoader("spell_oscillating_field") { }
|
|
|
|
class spell_oscillating_field_SpellScript : public SpellScript
|
|
{
|
|
PrepareSpellScript(spell_oscillating_field_SpellScript);
|
|
|
|
void HandleEffect(SpellEffIndex /*effIndex*/)
|
|
{
|
|
if (Player* player = GetHitPlayer())
|
|
if (player->GetAuraCount(SPELL_OSCILLATION_FIELD) == 5 && player->GetQuestStatus(QUEST_GAUGING_THE_RESONANT_FREQUENCY) == QUEST_STATUS_INCOMPLETE)
|
|
player->CompleteQuest(QUEST_GAUGING_THE_RESONANT_FREQUENCY);
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnEffectHitTarget += SpellEffectFn(spell_oscillating_field_SpellScript::HandleEffect, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
|
|
}
|
|
};
|
|
|
|
SpellScript* GetSpellScript() const override
|
|
{
|
|
return new spell_oscillating_field_SpellScript();
|
|
}
|
|
};
|
|
|
|
void AddSC_blades_edge_mountains()
|
|
{
|
|
// Ours
|
|
new npc_deaths_door_fell_cannon_target_bunny();
|
|
new npc_deaths_fel_cannon();
|
|
new spell_npc22275_crystal_prison();
|
|
// Theirs
|
|
new npc_nether_drake();
|
|
new npc_daranelle();
|
|
new npc_simon_bunny();
|
|
new go_simon_cluster();
|
|
new go_apexis_relic();
|
|
new npc_oscillating_frequency_scanner_master_bunny();
|
|
new spell_oscillating_field();
|
|
}
|