1055 lines
37 KiB
C++
1055 lines
37 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
|
|
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
|
*/
|
|
|
|
/* 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 "ScriptMgr.h"
|
|
#include "ScriptedCreature.h"
|
|
#include "ScriptedGossip.h"
|
|
#include "GridNotifiers.h"
|
|
#include "GridNotifiersImpl.h"
|
|
#include "Cell.h"
|
|
#include "CellImpl.h"
|
|
#include "SpellInfo.h"
|
|
#include "SpellScript.h"
|
|
#include "SpellAuras.h"
|
|
#include "SpellAuraEffects.h"
|
|
|
|
// Ours
|
|
enum deathsDoorWrapGate
|
|
{
|
|
SPELL_ARTILLERY_ON_THE_WRAP_GATE = 39221,
|
|
SPELL_ANTI_DEMON_FLAME_THROWER = 39222,
|
|
NPC_NORTH_GATE = 22471,
|
|
NPC_SOUTH_GATE = 22472,
|
|
NPC_NORTH_GATE_CREDIT = 22503,
|
|
NPC_SOUTH_GATE_CREDIT = 22504,
|
|
|
|
};
|
|
|
|
class npc_deahts_door_wrap_gate : public CreatureScript
|
|
{
|
|
public:
|
|
npc_deahts_door_wrap_gate() : CreatureScript("npc_deahts_door_wrap_gate") { }
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
return new npc_deahts_door_wrap_gateAI(creature);
|
|
}
|
|
|
|
struct npc_deahts_door_wrap_gateAI : public ScriptedAI
|
|
{
|
|
npc_deahts_door_wrap_gateAI(Creature *c) : ScriptedAI(c)
|
|
{
|
|
}
|
|
|
|
uint8 count;
|
|
uint32 timer;
|
|
void Reset() { count = 0; timer = 0;}
|
|
void SpellHit(Unit* caster, SpellInfo const* spellInfo)
|
|
{
|
|
if (spellInfo->Id == SPELL_ARTILLERY_ON_THE_WRAP_GATE)
|
|
{
|
|
Player* plr = caster->GetCharmerOrOwnerPlayerOrPlayerItself();
|
|
if (!plr)
|
|
return;
|
|
|
|
timer = 1;
|
|
count++;
|
|
|
|
if (count >= 6)
|
|
{
|
|
if (me->GetEntry() == NPC_SOUTH_GATE)
|
|
plr->KilledMonsterCredit(NPC_SOUTH_GATE_CREDIT, 0);
|
|
else
|
|
plr->KilledMonsterCredit(NPC_NORTH_GATE_CREDIT, 0);
|
|
|
|
Reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff)
|
|
{
|
|
if (timer)
|
|
{
|
|
timer += diff;
|
|
if (timer < 10000 || (timer >= 20000 && timer < 30000))
|
|
{
|
|
for (uint8 i = 0; i < 3; ++i)
|
|
if (Creature* cr = me->SummonCreature((roll_chance_i(50) ? 22474 : 22500), me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000))
|
|
if (Unit* target = cr->SelectNearbyTarget(NULL, 50.0f))
|
|
cr->AI()->AttackStart(target);
|
|
|
|
timer += 10000;
|
|
if (timer >= 30000)
|
|
timer = 0;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
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()
|
|
{
|
|
OnEffectPeriodic += AuraEffectPeriodicFn(spell_npc22275_crystal_prison_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
|
|
}
|
|
};
|
|
|
|
AuraScript* GetAuraScript() const
|
|
{
|
|
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()
|
|
{
|
|
IsNihil = false;
|
|
NihilSpeech_Timer = 3000;
|
|
NihilSpeech_Phase = 0;
|
|
|
|
ArcaneBlast_Timer = 7500;
|
|
ManaBurn_Timer = 10000;
|
|
IntangiblePresence_Timer = 15000;
|
|
}
|
|
|
|
void EnterCombat(Unit* /*who*/) { }
|
|
|
|
void MoveInLineOfSight(Unit* who)
|
|
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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
|
|
{
|
|
return new npc_nether_drakeAI(creature);
|
|
}
|
|
};
|
|
|
|
/*######
|
|
## npc_daranelle
|
|
######*/
|
|
|
|
enum Daranelle
|
|
{
|
|
SAY_SPELL_INFLUENCE = 0,
|
|
SPELL_LASHHAN_CHANNEL = 36904,
|
|
SPELL_DISPELLING_ANALYSIS = 37028
|
|
};
|
|
|
|
class npc_daranelle : public CreatureScript
|
|
{
|
|
public:
|
|
npc_daranelle() : CreatureScript("npc_daranelle") { }
|
|
|
|
struct npc_daranelleAI : public ScriptedAI
|
|
{
|
|
npc_daranelleAI(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void Reset() { }
|
|
|
|
void EnterCombat(Unit* /*who*/) { }
|
|
|
|
void MoveInLineOfSight(Unit* who)
|
|
|
|
{
|
|
if (who->GetTypeId() == TYPEID_PLAYER)
|
|
{
|
|
if (who->HasAura(SPELL_LASHHAN_CHANNEL) && me->IsWithinDistInMap(who, 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);
|
|
}
|
|
}
|
|
|
|
ScriptedAI::MoveInLineOfSight(who);
|
|
}
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
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;
|
|
uint64 playerGUID;
|
|
uint32 clusterIds[SIMON_MAX_COLORS];
|
|
float zCoordCorrection;
|
|
float searchDistance;
|
|
EventMap _events;
|
|
std::list<uint8> colorSequence, playableSequence, playerSequence;
|
|
|
|
void UpdateAI(uint32 diff)
|
|
{
|
|
_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)
|
|
{
|
|
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*/)
|
|
{
|
|
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(uint64 guid, int32 id)
|
|
{
|
|
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;
|
|
Trinity::AllWorldObjectsInRange objects(me, searchDistance);
|
|
Trinity::WorldObjectListSearcher<Trinity::AllWorldObjectsInRange> searcher(me, ClusterList, objects);
|
|
me->VisitNearbyObject(searchDistance, searcher);
|
|
|
|
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)
|
|
{
|
|
// 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, NULL, NULL, 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
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
player->PrepareGossipMenu(go, go->GetGOInfo()->questgiver.gossipID);
|
|
player->SendPreparedGossip(go);
|
|
return true;
|
|
}
|
|
|
|
bool OnGossipSelect(Player* player, GameObject* go, uint32 /*sender*/, uint32 /*action*/)
|
|
{
|
|
player->CLOSE_GOSSIP_MENU();
|
|
|
|
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 = 0;
|
|
timer = 500;
|
|
}
|
|
|
|
void Reset()
|
|
{
|
|
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() + 0.5f, 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)
|
|
{
|
|
if (summoner && summoner->isType(TYPEMASK_PLAYER))
|
|
playerGuid = summoner->GetGUID();
|
|
}
|
|
|
|
void UpdateAI(uint32 diff)
|
|
{
|
|
if (timer <= diff)
|
|
{
|
|
if (Player* player = ObjectAccessor::GetPlayer(*me, playerGuid))
|
|
DoCast(player, SPELL_OSCILLATION_FIELD);
|
|
|
|
timer = 3000;
|
|
}
|
|
else
|
|
timer -= diff;
|
|
}
|
|
|
|
private:
|
|
uint64 playerGuid;
|
|
uint32 timer;
|
|
};
|
|
|
|
CreatureAI* GetAI(Creature* creature) const
|
|
{
|
|
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()
|
|
{
|
|
OnEffectHitTarget += SpellEffectFn(spell_oscillating_field_SpellScript::HandleEffect, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
|
|
}
|
|
};
|
|
|
|
SpellScript* GetSpellScript() const
|
|
{
|
|
return new spell_oscillating_field_SpellScript();
|
|
}
|
|
};
|
|
|
|
void AddSC_blades_edge_mountains()
|
|
{
|
|
// Ours
|
|
new npc_deahts_door_wrap_gate();
|
|
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();
|
|
}
|