fix(Scripts/HyjalSummit): Rewrite Battle for Mount Hyjal (#18512)

* arrays

* summon_groups

* part 2 lesgooo

* holy frick it works???

* drafted

* forgot to add co-author

Co-Authored-By: Saltgurka <22568446+saltgurka@users.noreply.github.com>

* thrall & tyrande

* Update hyjal.cpp

* trash

* winterchill & anetheron

* kazrogal & azgalor

* Update rev_1708737709108426300.sql

* entrance rp and scourge base

* it worked thank you nyeriah and ayamiss the hunter from AQ20 cause I remembered that it had a similar mechanic and i went to check how to deal with it. did you know ayamiss was my turning point in my emulation "career"? it was a very interesting fight and i spent many hours researching it, leading to my discovery into how the swarmers work, if i did it nowadays i'd have found out immediately but it was a journey nonetheless

Co-Authored-By: Andrew <47818697+Nyeriah@users.noreply.github.com>

* Update rev_1708737709108426300.sql

* no gem farming!

* infernals

* leftovers

* doors

* archimonde

* Update rev_1708737709108426300.sql

* jaina didnt reset gossip flag its over

* crashfix

Co-Authored-By: Andrew <47818697+Nyeriah@users.noreply.github.com>

* Update instance_hyjal.cpp

* no more dberrors

* fix archimonde model and size

scale was set to 0.4, also fixed the speed and attack time, as well as adding model info

* Update rev_1708737709108426300.sql

* Update rev_1708737709108426300.sql

* more stuff

* Update data/sql/updates/pending_db_world/rev_1708737709108426300.sql

* fix: warning

* fix: warning

* buildfix

don't need to assign anything, as long as it doesn't return null we're good

* buildfix

infernal has no DoAction behaviour so it's fine

* Update data/sql/updates/pending_db_world/rev_1708737709108426300.sql

* Update data/sql/updates/pending_db_world/rev_1708737709108426300.sql

* yay

Co-Authored-By: Dan <83884799+elthehablo@users.noreply.github.com>

* a

* looks nicer, less updates overall

* crashfix?

messy code gomenasai 🙇

* hide undesirables >:(

* Update instance_hyjal.cpp

Co-Authored-By: Anton Popovichenko <walkline.ua@gmail.com>

---------

Co-authored-by: Gultask <sagemochi@hotmail.com>
Co-authored-by: Saltgurka <22568446+saltgurka@users.noreply.github.com>
Co-authored-by: Andrew <47818697+Nyeriah@users.noreply.github.com>
Co-authored-by: Gultask <100873791+Gultask@users.noreply.github.com>
Co-authored-by: Dan <83884799+elthehablo@users.noreply.github.com>
Co-authored-by: Anton Popovichenko <walkline.ua@gmail.com>
This commit is contained in:
Stefano Borzì
2024-03-19 21:49:28 +01:00
committed by GitHub
parent e98fdfd04e
commit ec1b2f7a28
14 changed files with 3754 additions and 3939 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -18,16 +18,16 @@
#include "CreatureScript.h"
#include "ScriptedCreature.h"
#include "hyjal.h"
#include "hyjal_trash.h"
enum Spells
{
SPELL_CARRION_SWARM = 31306,
SPELL_SLEEP = 31298,
SPELL_VAMPIRIC_AURA = 38196,
SPELL_INFERNO = 31299,
SPELL_IMMOLATION = 31303,
SPELL_INFERNO_EFFECT = 31302,
SPELL_CARRION_SWARM = 31306,
SPELL_SLEEP = 31298,
SPELL_INFERNO = 31299,
SPELL_VAMPIRIC_AURA = 31317,
SPELL_ENRAGE = 26662,
SPELL_INFERNAL_STUN = 31302,
SPELL_INFERNAL_IMMOLATION = 31304
};
enum Texts
@@ -37,232 +37,108 @@ enum Texts
SAY_SWARM = 2,
SAY_SLEEP = 3,
SAY_INFERNO = 4,
SAY_ONAGGRO = 5,
SAY_ONSPAWN = 5,
};
enum Misc
{
PATH_ANETHERON = 178080,
POINT_COMBAT_START = 7
};
class boss_anetheron : public CreatureScript
struct boss_anetheron : public BossAI
{
public:
boss_anetheron() : CreatureScript("boss_anetheron") { }
CreatureAI* GetAI(Creature* creature) const override
boss_anetheron(Creature* creature) : BossAI(creature, DATA_ANETHERON)
{
return GetHyjalAI<boss_anetheronAI>(creature);
_recentlySpoken = false;
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_anetheronAI : public hyjal_trashAI
void JustEngagedWith(Unit * who) override
{
boss_anetheronAI(Creature* creature) : hyjal_trashAI(creature)
BossAI::JustEngagedWith(who);
scheduler.Schedule(20s, 28s, [this](TaskContext context)
{
instance = creature->GetInstanceScript();
go = false;
}
uint32 SwarmTimer;
uint32 SleepTimer;
uint32 AuraTimer;
uint32 InfernoTimer;
bool go;
void Reset() override
{
damageTaken = 0;
SwarmTimer = 45000;
SleepTimer = 60000;
AuraTimer = 5000;
InfernoTimer = 45000;
if (IsEvent)
instance->SetData(DATA_ANETHERONEVENT, NOT_STARTED);
}
void JustEngagedWith(Unit* /*who*/) override
{
if (IsEvent)
instance->SetData(DATA_ANETHERONEVENT, IN_PROGRESS);
Talk(SAY_ONAGGRO);
}
void KilledUnit(Unit* who) override
{
if (who->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_ONSLAY);
}
void WaypointReached(uint32 waypointId) override
{
if (waypointId == POINT_COMBAT_START)
{
Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE));
if (target && target->IsAlive())
me->AddThreat(target, 0.0f);
}
}
void JustDied(Unit* killer) override
{
hyjal_trashAI::JustDied(killer);
if (IsEvent)
instance->SetData(DATA_ANETHERONEVENT, DONE);
Talk(SAY_ONDEATH);
}
void UpdateAI(uint32 diff) override
{
if (IsEvent)
{
//Must update npc_escortAI
npc_escortAI::UpdateAI(diff);
if (!go)
{
go = true;
me->GetMotionMaster()->MovePath(PATH_ANETHERON, false);
}
}
//Return since we have no target
if (!UpdateVictim())
{
return;
}
if (SwarmTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
{
DoCast(target, SPELL_CARRION_SWARM);
}
SwarmTimer = urand(45000, 60000);
if (DoCastRandomTarget(SPELL_CARRION_SWARM, 0, 60.f))
Talk(SAY_SWARM);
}
else SwarmTimer -= diff;
if (SleepTimer <= diff)
{
for (uint8 i = 0; i < 3; ++i)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
target->CastSpell(target, SPELL_SLEEP, true);
}
SleepTimer = 60000;
context.Repeat(10s, 15s);
}).Schedule(25s, 32s, [this](TaskContext context)
{
if (DoCastRandomTarget(SPELL_SLEEP))
Talk(SAY_SLEEP);
}
else SleepTimer -= diff;
if (AuraTimer <= diff)
{
DoCast(me, SPELL_VAMPIRIC_AURA, true);
AuraTimer = urand(10000, 20000);
}
else AuraTimer -= diff;
if (InfernoTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 0, 100, true), SPELL_INFERNO);
InfernoTimer = 45000;
context.Repeat(35s, 48s);
}).Schedule(30s, 48s, [this](TaskContext context)
{
if (DoCastRandomTarget(SPELL_INFERNO))
Talk(SAY_INFERNO);
}
else InfernoTimer -= diff;
DoMeleeAttackIfReady();
}
};
};
class npc_towering_infernal : public CreatureScript
{
public:
npc_towering_infernal() : CreatureScript("npc_towering_infernal") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetHyjalAI<npc_towering_infernalAI>(creature);
context.Repeat(50s, 55s);
}).Schedule(10min, [this](TaskContext context)
{
DoCastSelf(SPELL_ENRAGE);
context.Repeat(5min);
});
}
struct npc_towering_infernalAI : public ScriptedAI
void JustSummoned(Creature* summon) override
{
npc_towering_infernalAI(Creature* creature) : ScriptedAI(creature)
if (summon)
{
instance = creature->GetInstanceScript();
AnetheronGUID = instance->GetGuidData(DATA_ANETHERON);
summon->AI()->DoCast(SPELL_INFERNAL_STUN);
summon->SetInCombatWithZone();
}
BossAI::JustSummoned(summon);
}
uint32 ImmolationTimer;
uint32 CheckTimer;
ObjectGuid AnetheronGUID;
InstanceScript* instance;
void DoAction(int32 action) override
{
Talk(SAY_ONSPAWN, 1200ms);
void Reset() override
if (action == DATA_ANETHERON)
me->GetMotionMaster()->MovePath(urand(ALLIANCE_BASE_CHARGE_1, ALLIANCE_BASE_CHARGE_3), false);
}
void PathEndReached(uint32 pathId) override
{
switch (pathId)
{
DoCast(me, SPELL_INFERNO_EFFECT);
ImmolationTimer = 5000;
CheckTimer = 5000;
}
void JustEngagedWith(Unit* /*who*/) override
{
}
void KilledUnit(Unit* /*victim*/) override
{
}
void JustDied(Unit* /*killer*/) override
{
}
void MoveInLineOfSight(Unit* who) override
{
if (me->IsWithinDist(who, 50) && !me->IsInCombat() && me->IsValidAttackTarget(who))
{
AttackStart(who);
}
}
void UpdateAI(uint32 diff) override
{
if (CheckTimer <= diff)
{
if (AnetheronGUID)
case ALLIANCE_BASE_CHARGE_1:
case ALLIANCE_BASE_CHARGE_2:
case ALLIANCE_BASE_CHARGE_3:
me->m_Events.AddEventAtOffset([this]()
{
Creature* boss = ObjectAccessor::GetCreature(*me, AnetheronGUID);
if (!boss || boss->isDead())
{
me->setDeathState(DeathState::JustDied);
me->RemoveCorpse();
return;
}
}
CheckTimer = 5000;
}
else CheckTimer -= diff;
//Return since we have no target
if (!UpdateVictim())
{
return;
}
if (ImmolationTimer <= diff)
{
DoCast(me, SPELL_IMMOLATION);
ImmolationTimer = 5000;
}
else ImmolationTimer -= diff;
DoMeleeAttackIfReady();
me->GetMotionMaster()->MovePath(urand(ALLIANCE_BASE_PATROL_1, ALLIANCE_BASE_PATROL_3), true);
}, 1s);
break;
}
};
}
void KilledUnit(Unit* victim) override
{
if (!_recentlySpoken && victim->IsPlayer())
{
Talk(SAY_ONSLAY);
_recentlySpoken = true;
scheduler.Schedule(6s, [this](TaskContext)
{
_recentlySpoken = false;
});
}
}
void JustDied(Unit * killer) override
{
Talk(SAY_ONDEATH);
BossAI::JustDied(killer);
}
private:
bool _recentlySpoken;
};
void AddSC_boss_anetheron()
{
new boss_anetheron();
new npc_towering_infernal();
RegisterHyjalAI(boss_anetheron);
}

View File

@@ -22,12 +22,6 @@
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "hyjal.h"
/* ScriptData
SDName: Boss_Archimonde
SD%Complete: 85
SDComment: Doomfires not completely offlike due to core limitations for random moving. Tyrande and second phase not fully implemented.
SDCategory: Caverns of Time, Mount Hyjal
EndScriptData */
enum Texts
{
@@ -40,7 +34,7 @@ enum Texts
SAY_SOUL_CHARGE = 7,
};
enum Spells
enum ArchiSpells
{
SPELL_DENOUEMENT_WISP = 32124,
SPELL_ANCIENT_SPARK = 39349,
@@ -290,7 +284,7 @@ public:
void Reset() override
{
instance->SetData(DATA_ARCHIMONDEEVENT, NOT_STARTED);
instance->SetData(DATA_ARCHIMONDE, NOT_STARTED);
me->SetReactState(REACT_AGGRESSIVE);
DoomfireSpiritGUID.Clear();
@@ -354,12 +348,14 @@ public:
Talk(SAY_AGGRO);
DoZoneInCombat();
instance->SetData(DATA_ARCHIMONDEEVENT, IN_PROGRESS);
instance->SetData(DATA_ARCHIMONDE, IN_PROGRESS);
events.ScheduleEvent(EVENT_SPELL_AIR_BURST, urand(25000, 35000));
events.ScheduleEvent(EVENT_SPELL_DOOMFIRE, urand(10000, 20000));
events.ScheduleEvent(EVENT_SPELL_FEAR, 42000);
events.ScheduleEvent(EVENT_SPELL_GRIP_OF_THE_LEGION, 2000);
events.ScheduleEvent(EVENT_SPELL_FINGER_OF_DEATH, 1000);
instance->SetData(DATA_SPAWN_WAVES, 1);
}
void KilledUnit(Unit* victim) override
@@ -399,7 +395,7 @@ public:
{
Talk(SAY_DEATH);
instance->SetData(DATA_ARCHIMONDEEVENT, DONE);
instance->SetData(DATA_ARCHIMONDE, DONE);
instance->DoUpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, me->GetEntry(), 1, me);
// Reset scheduled events
@@ -418,6 +414,12 @@ public:
summons.DespawnAll();
}
void EnterEvadeMode(EvadeReason why) override
{
instance->SetData(DATA_RESET_NIGHT_ELF, 1);
BossAI::EnterEvadeMode(why);
}
bool CanUseFingerOfDeath()
{
// Cast finger of death below 10% health
@@ -553,13 +555,13 @@ public:
if (!me->IsInCombat())
{
// Do not let the raid skip straight to Archimonde. Visible and hostile ONLY if Azagalor is finished.
if ((instance->GetData(DATA_AZGALOREVENT) < DONE) && (me->IsVisible() || (me->GetFaction() != FACTION_FRIENDLY)))
if ((instance->GetBossState(DATA_AZGALOR) != DONE) && (me->IsVisible() || (me->GetFaction() != FACTION_FRIENDLY)))
{
me->SetVisible(false);
me->SetFaction(FACTION_FRIENDLY);
}
if ((instance->GetData(DATA_AZGALOREVENT) >= DONE) && (!me->IsVisible() || (me->GetFaction() == FACTION_FRIENDLY)))
if ((instance->GetBossState(DATA_AZGALOR) == DONE) && (!me->IsVisible() || (me->GetFaction() == FACTION_FRIENDLY)))
{
me->SetFaction(FACTION_DRAGONKIN);
me->SetVisible(true);

View File

@@ -18,7 +18,6 @@
#include "CreatureScript.h"
#include "ScriptedCreature.h"
#include "hyjal.h"
#include "hyjal_trash.h"
enum Spells
{
@@ -26,11 +25,7 @@ enum Spells
SPELL_DOOM = 31347,
SPELL_HOWL_OF_AZGALOR = 31344,
SPELL_CLEAVE = 31345,
SPELL_BERSERK = 26662,
SPELL_THRASH = 12787,
SPELL_CRIPPLE = 31406,
SPELL_WARSTOMP = 31408,
SPELL_BERSERK = 26662
};
enum Texts
@@ -38,249 +33,88 @@ enum Texts
SAY_ONDEATH = 0,
SAY_ONSLAY = 1,
SAY_DOOM = 2, // Not used?
SAY_ONAGGRO = 3,
SAY_ONSPAWN = 3,
SAY_ARCHIMONDE_INTRO = 8
};
class boss_azgalor : public CreatureScript
struct boss_azgalor : public BossAI
{
public:
boss_azgalor() : CreatureScript("boss_azgalor") { }
CreatureAI* GetAI(Creature* creature) const override
boss_azgalor(Creature* creature) : BossAI(creature, DATA_AZGALOR)
{
return GetHyjalAI<boss_azgalorAI>(creature);
_recentlySpoken = false;
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_azgalorAI : public hyjal_trashAI
void JustEngagedWith(Unit * who) override
{
boss_azgalorAI(Creature* creature) : hyjal_trashAI(creature)
BossAI::JustEngagedWith(who);
scheduler.Schedule(10s, 16s, [this](TaskContext context)
{
instance = creature->GetInstanceScript();
go = false;
}
uint32 RainTimer;
uint32 DoomTimer;
uint32 HowlTimer;
uint32 CleaveTimer;
uint32 EnrageTimer;
bool enraged;
bool go;
void Reset() override
DoCastVictim(SPELL_CLEAVE);
context.Repeat(8s, 16s);
}).Schedule(25s, [this](TaskContext context)
{
damageTaken = 0;
RainTimer = 20000;
DoomTimer = 50000;
HowlTimer = 30000;
CleaveTimer = 10000;
EnrageTimer = 600000;
enraged = false;
if (IsEvent)
instance->SetData(DATA_AZGALOREVENT, NOT_STARTED);
}
void JustEngagedWith(Unit* /*who*/) override
DoCastRandomTarget(SPELL_RAIN_OF_FIRE, 0, 40.f);
context.Repeat(15s);
}).Schedule(30s, [this](TaskContext context)
{
if (IsEvent)
instance->SetData(DATA_AZGALOREVENT, IN_PROGRESS);
DoCastAOE(SPELL_HOWL_OF_AZGALOR);
context.Repeat(18s, 20s);
}).Schedule(45s, 55s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_DOOM, 0, 100.f);
Talk(SAY_DOOM);
context.Repeat();
}).Schedule(10min, [this](TaskContext context)
{
DoCastSelf(SPELL_BERSERK);
context.Repeat(5min);
});
}
Talk(SAY_ONAGGRO);
}
void DoAction(int32 action) override
{
Talk(SAY_ONSPAWN, 1200ms);
void KilledUnit(Unit* /*victim*/) override
if (action == DATA_AZGALOR)
me->GetMotionMaster()->MovePath(HORDE_BOSS_PATH, false);
}
void KilledUnit(Unit * victim) override
{
if (!_recentlySpoken && victim->IsPlayer())
{
Talk(SAY_ONSLAY);
_recentlySpoken = true;
scheduler.Schedule(6s, [this](TaskContext)
{
_recentlySpoken = false;
});
}
void WaypointReached(uint32 waypointId) override
{
if (waypointId == 7 && instance)
{
Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL));
if (target && target->IsAlive())
me->AddThreat(target, 0.0f);
}
}
void JustDied(Unit* killer) override
{
hyjal_trashAI::JustDied(killer);
if (IsEvent)
instance->SetData(DATA_AZGALOREVENT, DONE);
Talk(SAY_ONDEATH);
}
void UpdateAI(uint32 diff) override
{
if (IsEvent)
{
//Must update npc_escortAI
npc_escortAI::UpdateAI(diff);
if (!go)
{
go = true;
AddWaypoint(0, 5492.91f, -2404.61f, 1462.63f);
AddWaypoint(1, 5531.76f, -2460.87f, 1469.55f);
AddWaypoint(2, 5554.58f, -2514.66f, 1476.12f);
AddWaypoint(3, 5554.16f, -2567.23f, 1479.90f);
AddWaypoint(4, 5540.67f, -2625.99f, 1480.89f);
AddWaypoint(5, 5508.16f, -2659.2f, 1480.15f);
AddWaypoint(6, 5489.62f, -2704.05f, 1482.18f);
AddWaypoint(7, 5457.04f, -2726.26f, 1485.10f);
Start(false, true);
SetDespawnAtEnd(false);
}
}
//Return since we have no target
if (!UpdateVictim())
return;
if (RainTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 0, 30, true), SPELL_RAIN_OF_FIRE);
RainTimer = 20000 + rand() % 15000;
}
else RainTimer -= diff;
if (DoomTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 1, 100, true), SPELL_DOOM);//never on tank
DoomTimer = 45000 + rand() % 5000;
}
else DoomTimer -= diff;
if (HowlTimer <= diff)
{
DoCast(me, SPELL_HOWL_OF_AZGALOR);
HowlTimer = 30000;
}
else HowlTimer -= diff;
if (CleaveTimer <= diff)
{
DoCastVictim(SPELL_CLEAVE);
CleaveTimer = 10000 + rand() % 5000;
}
else CleaveTimer -= diff;
if (EnrageTimer < diff && !enraged)
{
me->InterruptNonMeleeSpells(false);
DoCast(me, SPELL_BERSERK, true);
enraged = true;
EnrageTimer = 600000;
}
else EnrageTimer -= diff;
DoMeleeAttackIfReady();
}
};
};
class npc_lesser_doomguard : public CreatureScript
{
public:
npc_lesser_doomguard() : CreatureScript("npc_lesser_doomguard") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetHyjalAI<npc_lesser_doomguardAI>(creature);
}
struct npc_lesser_doomguardAI : public hyjal_trashAI
void JustDied(Unit * killer) override
{
npc_lesser_doomguardAI(Creature* creature) : hyjal_trashAI(creature)
{
instance = creature->GetInstanceScript();
AzgalorGUID = instance->GetGuidData(DATA_AZGALOR);
}
Talk(SAY_ONDEATH);
// If Archimonde has not yet been initialized, this won't trigger
if (Creature* archi = instance->GetCreature(DATA_ARCHIMONDE))
archi->AI()->Talk(SAY_ARCHIMONDE_INTRO, 25000ms);
BossAI::JustDied(killer);
}
uint32 CrippleTimer;
uint32 WarstompTimer;
uint32 CheckTimer;
ObjectGuid AzgalorGUID;
InstanceScript* instance;
private:
bool _recentlySpoken;
void Reset() override
{
CrippleTimer = 50000;
WarstompTimer = 10000;
DoCast(me, SPELL_THRASH);
CheckTimer = 5000;
}
void JustEngagedWith(Unit* /*who*/) override
{
}
void KilledUnit(Unit* /*victim*/) override
{
}
void WaypointReached(uint32 /*waypointId*/) override
{
}
void MoveInLineOfSight(Unit* who) override
{
if (me->IsWithinDist(who, 50) && !me->IsInCombat() && me->IsValidAttackTarget(who))
AttackStart(who);
}
void JustDied(Unit* /*killer*/) override
{
}
void UpdateAI(uint32 diff) override
{
if (CheckTimer <= diff)
{
if (AzgalorGUID)
{
Creature* boss = ObjectAccessor::GetCreature(*me, AzgalorGUID);
if (!boss || boss->isDead())
{
me->setDeathState(DeathState::JustDied);
me->RemoveCorpse();
return;
}
}
CheckTimer = 5000;
}
else CheckTimer -= diff;
//Return since we have no target
if (!UpdateVictim())
{
return;
}
if (WarstompTimer <= diff)
{
DoCast(me, SPELL_WARSTOMP);
WarstompTimer = 10000 + rand() % 5000;
}
else WarstompTimer -= diff;
if (CrippleTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 0, 100, true), SPELL_CRIPPLE);
CrippleTimer = 25000 + rand() % 5000;
}
else CrippleTimer -= diff;
DoMeleeAttackIfReady();
}
};
};
void AddSC_boss_azgalor()
{
new boss_azgalor();
new npc_lesser_doomguard();
RegisterHyjalAI(boss_azgalor);
}

View File

@@ -21,21 +21,21 @@
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "hyjal.h"
#include "hyjal_trash.h"
enum Spells
{
SPELL_CLEAVE = 31436,
SPELL_WARSTOMP = 31480,
SPELL_MARK = 31447,
SPELL_MARK_DAMAGE = 31463
SPELL_MALEVOLENT_CLEAVE = 31436,
SPELL_WAR_STOMP = 31480,
SPELL_CRIPPLE = 31477,
SPELL_MARK = 31447,
SPELL_MARK_DAMAGE = 31463
};
enum Texts
{
SAY_ONSLAY = 0,
SAY_MARK = 1,
SAY_ONAGGRO = 2,
SAY_ONSPAWN = 2,
};
enum Sounds
@@ -43,124 +43,88 @@ enum Sounds
SOUND_ONDEATH = 11018,
};
enum Misc
{
PATH_KAZROGAL = 178880,
POINT_COMBAT_START = 7
};
class boss_kazrogal : public CreatureScript
struct boss_kazrogal : public BossAI
{
public:
boss_kazrogal() : CreatureScript("boss_kazrogal") { }
CreatureAI* GetAI(Creature* creature) const override
boss_kazrogal(Creature* creature) : BossAI(creature, DATA_KAZROGAL)
{
return GetHyjalAI<boss_kazrogalAI>(creature);
_recentlySpoken = false;
_markCounter = 0;
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_kazrogalAI : public hyjal_trashAI
void JustEngagedWith(Unit * who) override
{
boss_kazrogalAI(Creature* creature) : hyjal_trashAI(creature)
BossAI::JustEngagedWith(who);
scheduler.Schedule(6s, 21s, [this](TaskContext context)
{
instance = creature->GetInstanceScript();
go = false;
}
uint32 CleaveTimer;
uint32 WarStompTimer;
uint32 MarkTimer;
uint32 MarkTimerBase;
bool go;
void Reset() override
DoCastVictim(SPELL_MALEVOLENT_CLEAVE);
context.Repeat();
}).Schedule(12s, 18s, [this](TaskContext context)
{
damageTaken = 0;
CleaveTimer = 5000;
WarStompTimer = 15000;
MarkTimer = 45000;
MarkTimerBase = 45000;
if (IsEvent)
instance->SetData(DATA_KAZROGALEVENT, NOT_STARTED);
}
void JustEngagedWith(Unit* /*who*/) override
if (SelectTarget(SelectTargetMethod::Random, 0, 12.f))
{
DoCastAOE(SPELL_WAR_STOMP);
context.Repeat(15s, 30s);
}
else
context.Repeat(1200ms);
}).Schedule(15s, [this](TaskContext context)
{
if (IsEvent)
instance->SetData(DATA_KAZROGALEVENT, IN_PROGRESS);
Talk(SAY_ONAGGRO);
}
DoCastRandomTarget(SPELL_CRIPPLE, 0, 20.f);
context.Repeat(12s, 20s);
}).Schedule(45s, [this](TaskContext context)
{
DoCastSelf(SPELL_MARK);
Talk(SAY_MARK);
context.Repeat(GetMarkRepeatTimer());
});
}
void KilledUnit(Unit* /*victim*/) override
Milliseconds GetMarkRepeatTimer()
{
Milliseconds timer = 45000ms - (5000ms * _markCounter);
if (timer <= 10000ms)
return 10000ms;
else
return timer;
}
void DoAction(int32 action) override
{
Talk(SAY_ONSPAWN, 1200ms);
if (action == DATA_KAZROGAL)
me->GetMotionMaster()->MovePath(HORDE_BOSS_PATH, false);
}
void KilledUnit(Unit * victim) override
{
if (!_recentlySpoken && victim->IsPlayer())
{
Talk(SAY_ONSLAY);
}
_recentlySpoken = true;
void WaypointReached(uint32 waypointId) override
{
if (waypointId == POINT_COMBAT_START && instance)
{
Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL));
if (target && target->IsAlive())
me->AddThreat(target, 0.0f);
}
}
void JustDied(Unit* killer) override
{
hyjal_trashAI::JustDied(killer);
if (IsEvent)
instance->SetData(DATA_KAZROGALEVENT, DONE);
DoPlaySoundToSet(me, SOUND_ONDEATH);
}
void UpdateAI(uint32 diff) override
{
if (IsEvent)
{
//Must update npc_escortAI
npc_escortAI::UpdateAI(diff);
if (!go)
scheduler.Schedule(6s, [this](TaskContext)
{
go = true;
me->GetMotionMaster()->MovePath(PATH_KAZROGAL, false);
}
}
//Return since we have no target
if (!UpdateVictim())
return;
if (CleaveTimer <= diff)
{
DoCast(me, SPELL_CLEAVE);
CleaveTimer = 6000 + rand() % 15000;
}
else CleaveTimer -= diff;
if (WarStompTimer <= diff)
{
DoCast(me, SPELL_WARSTOMP);
WarStompTimer = 60000;
}
else WarStompTimer -= diff;
if (MarkTimer <= diff)
{
DoCastAOE(SPELL_MARK);
MarkTimerBase -= 5000;
if (MarkTimerBase < 5500)
MarkTimerBase = 5500;
MarkTimer = MarkTimerBase;
Talk(SAY_MARK);
}
else MarkTimer -= diff;
DoMeleeAttackIfReady();
_recentlySpoken = false;
});
}
};
}
void JustDied(Unit * killer) override
{
me->PlayDirectSound(SOUND_ONDEATH);
BossAI::JustDied(killer);
}
private:
bool _recentlySpoken;
uint8 _markCounter;
};
class spell_mark_of_kazrogal : public SpellScriptLoader
@@ -223,7 +187,7 @@ public:
void AddSC_boss_kazrogal()
{
new boss_kazrogal();
RegisterHyjalAI(boss_kazrogal);
new spell_mark_of_kazrogal();
}

View File

@@ -18,14 +18,14 @@
#include "CreatureScript.h"
#include "ScriptedCreature.h"
#include "hyjal.h"
#include "hyjal_trash.h"
enum Spells
{
SPELL_FROST_ARMOR = 31256,
SPELL_DEATH_AND_DECAY = 31258,
SPELL_FROST_NOVA = 31250,
SPELL_ICEBOLT = 31249
SPELL_ICEBOLT = 31249,
SPELL_ENRAGE = 26662
};
enum Texts
@@ -34,7 +34,7 @@ enum Texts
SAY_ONSLAY = 1,
SAY_DECAY = 2,
SAY_NOVA = 3,
SAY_ONAGGRO = 4
SAY_ONSPAWN = 4
};
enum Misc
@@ -43,122 +43,101 @@ enum Misc
POINT_COMBAT_START = 7
};
class boss_rage_winterchill : public CreatureScript
struct boss_rage_winterchill : public BossAI
{
public:
boss_rage_winterchill() : CreatureScript("boss_rage_winterchill") { }
CreatureAI* GetAI(Creature* creature) const override
boss_rage_winterchill(Creature* creature) : BossAI(creature, DATA_WINTERCHILL)
{
return GetHyjalAI<boss_rage_winterchillAI>(creature);
_recentlySpoken = false;
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_rage_winterchillAI : public hyjal_trashAI
void JustEngagedWith(Unit* who) override
{
boss_rage_winterchillAI(Creature* creature) : hyjal_trashAI(creature)
BossAI::JustEngagedWith(who);
scheduler.Schedule(18s, 24s, [this](TaskContext context)
{
instance = creature->GetInstanceScript();
go = false;
}
uint32 FrostArmorTimer;
uint32 DecayTimer;
uint32 NovaTimer;
uint32 IceboltTimer;
bool go;
void Reset() override
DoCastSelf(SPELL_FROST_ARMOR);
context.Repeat(30s, 45s);
}).Schedule(5s, 9s, [this](TaskContext context)
{
damageTaken = 0;
FrostArmorTimer = 37000;
DecayTimer = 45000;
NovaTimer = 15000;
IceboltTimer = 10000;
if (IsEvent)
instance->SetData(DATA_RAGEWINTERCHILLEVENT, NOT_STARTED);
}
void JustEngagedWith(Unit* /*who*/) override
DoCastRandomTarget(SPELL_ICEBOLT);
context.Repeat(9s, 15s);
}).Schedule(12s, 17s, [this](TaskContext context)
{
if (IsEvent)
instance->SetData(DATA_RAGEWINTERCHILLEVENT, IN_PROGRESS);
Talk(SAY_ONAGGRO);
}
if (SelectTarget(SelectTargetMethod::Random, 0, 20.f))
{
DoCastAOE(SPELL_FROST_NOVA);
Talk(SAY_NOVA);
context.Repeat(25s, 30s);
}
else
context.Repeat(1200ms);
}).Schedule(21s, 28s, [this](TaskContext context)
{
if (DoCastRandomTarget(SPELL_DEATH_AND_DECAY, 0, 40.f))
Talk(SAY_DECAY);
void KilledUnit(Unit* /*victim*/) override
context.Repeat(45s);
}).Schedule(10min, [this](TaskContext context)
{
DoCastSelf(SPELL_ENRAGE);
context.Repeat(5min);
});
}
void DoAction(int32 action) override
{
Talk(SAY_ONSPAWN, 1200ms);
if (action == DATA_WINTERCHILL)
me->GetMotionMaster()->MovePath(urand(ALLIANCE_BASE_CHARGE_1, ALLIANCE_BASE_CHARGE_3), false);
}
void PathEndReached(uint32 pathId) override
{
switch (pathId)
{
case ALLIANCE_BASE_CHARGE_1:
case ALLIANCE_BASE_CHARGE_2:
case ALLIANCE_BASE_CHARGE_3:
me->m_Events.AddEventAtOffset([this]()
{
me->GetMotionMaster()->MovePath(urand(ALLIANCE_BASE_PATROL_1, ALLIANCE_BASE_PATROL_3), true);
}, 1s);
break;
}
}
void KilledUnit(Unit* victim) override
{
if (!_recentlySpoken && victim->IsPlayer())
{
Talk(SAY_ONSLAY);
}
_recentlySpoken = true;
void WaypointReached(uint32 waypointId) override
{
if (waypointId == POINT_COMBAT_START && instance)
{
Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE));
if (target && target->IsAlive())
me->AddThreat(target, 0.0f);
}
}
void JustDied(Unit* killer) override
{
hyjal_trashAI::JustDied(killer);
if (IsEvent)
instance->SetData(DATA_RAGEWINTERCHILLEVENT, DONE);
Talk(SAY_ONDEATH);
}
void UpdateAI(uint32 diff) override
{
if (IsEvent)
{
//Must update npc_escortAI
npc_escortAI::UpdateAI(diff);
if (!go)
scheduler.Schedule(6s, [this](TaskContext)
{
go = true;
me->GetMotionMaster()->MovePath(PATH_RAGE_WINTERCHILL, false);
}
}
//Return since we have no target
if (!UpdateVictim())
return;
if (FrostArmorTimer <= diff)
{
DoCast(me, SPELL_FROST_ARMOR);
FrostArmorTimer = 40000 + rand() % 20000;
}
else FrostArmorTimer -= diff;
if (DecayTimer <= diff)
{
DoCastVictim(SPELL_DEATH_AND_DECAY);
DecayTimer = 60000 + rand() % 20000;
Talk(SAY_DECAY);
}
else DecayTimer -= diff;
if (NovaTimer <= diff)
{
DoCastVictim(SPELL_FROST_NOVA);
NovaTimer = 30000 + rand() % 15000;
Talk(SAY_NOVA);
}
else NovaTimer -= diff;
if (IceboltTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 0, 40, true), SPELL_ICEBOLT);
IceboltTimer = 11000 + rand() % 20000;
}
else IceboltTimer -= diff;
DoMeleeAttackIfReady();
_recentlySpoken = false;
});
}
};
}
void JustDied(Unit* killer) override
{
Talk(SAY_ONDEATH);
BossAI::JustDied(killer);
}
private:
bool _recentlySpoken;
};
void AddSC_boss_rage_winterchill()
{
new boss_rage_winterchill();
RegisterHyjalAI(boss_rage_winterchill);
}

View File

@@ -15,244 +15,656 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Hyjal
SD%Complete: 80
SDComment: gossip text id's unknown
SDCategory: Caverns of Time, Mount Hyjal
EndScriptData */
/* ContentData
npc_jaina_proudmoore
npc_thrall
npc_tyrande_whisperwind
EndContentData */
#include "CreatureScript.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "hyjalAI.h"
#include "hyjal.h"
#define GOSSIP_ITEM_BEGIN_ALLY "My companions and I are with you, Lady Proudmoore."
#define GOSSIP_ITEM_ANETHERON "We are ready for whatever Archimonde might send our way, Lady Proudmoore."
enum Spells
{
// Jaina
SPELL_MASS_TELEPORT = 16807,
SPELL_SIMPLE_TELEPORT = 12980,
SPELL_SALVATION = 31745,
SPELL_BRILLIANCE_AURA = 31260,
SPELL_BLIZZARD = 31266,
SPELL_PYROBLAST = 31263,
SPELL_SUMMON_ELEMENTALS = 31264,
#define GOSSIP_ITEM_BEGIN_HORDE "I am with you, Thrall."
#define GOSSIP_ITEM_AZGALOR "We have nothing to fear."
// Thrall
SPELL_CHAIN_LIGHTNING = 31330,
SPELL_FERAL_SPIRIT = 31331,
#define GOSSIP_ITEM_RETREAT "We can't keep this up. Let's retreat!"
// Tyrande
SPELL_STARFALL = 20687,
SPELL_TRUESHOT_AURA = 31519,
SPELL_SUMMON_TEARS_OF_THE_GODDESS = 39118,
#define GOSSIP_ITEM_TYRANDE "Aid us in defending Nordrassil"
#define ITEM_TEAR_OF_GODDESS 24494
// Ghoul
SPELL_FRENZY = 31540,
SPELL_CANNIBALIZE = 31537,
#define GOSSIP_ITEM_GM1 "[GM] Toggle Debug Timers"
// Crypt Fiend
SPELL_CRYPT_SCARABS = 31592,
class npc_jaina_proudmoore : public CreatureScript
// Abomination
SPELL_KNOCKDOWN = 31610,
// Necromancer (Ranged)
SPELL_RAISE_DEAD_1 = 31617,
SPELL_RAISE_DEAD_2 = 31624,
SPELL_RAISE_DEAD_3 = 31625,
SPELL_SHADOW_BOLT = 31627,
// Banshee (Ranged)
SPELL_BANSHEE_CURSE = 31651,
SPELL_ANTI_MAGIC_SHELL = 31662,
SPELL_BANSHEE_WAIL = 38183,
// Gargoyle (Ranged)
SPELL_GARGOYLE_STRIKE = 31664,
// Frost Wyrm (Ranged)
SPELL_FROST_BREATH = 31688,
// Fel Stalker
SPELL_MANA_BURN = 31729
};
enum Talk
{
SAY_ATTACKED = 0,
SAY_BEGIN = 1,
SAY_INCOMING = 2,
SAY_RALLY = 3,
SAY_FAILURE = 4,
SAY_SUCCESS = 5,
SAY_DEATH = 6,
SAY_TELEPORT = 7
};
class npc_hyjal_jaina : public CreatureScript
{
public:
npc_jaina_proudmoore() : CreatureScript("npc_jaina_proudmoore") { }
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
hyjalAI* ai = CAST_AI(hyjalAI, creature->AI());
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1:
ai->StartEvent(player);
break;
case GOSSIP_ACTION_INFO_DEF + 2:
ai->FirstBossDead = true;
ai->WaveCount = 9;
ai->StartEvent(player);
break;
case GOSSIP_ACTION_INFO_DEF + 3:
ai->Retreat();
break;
case GOSSIP_ACTION_INFO_DEF:
ai->Debug = !ai->Debug;
//LOG_DEBUG("scripts", "HyjalAI - Debug mode has been toggled");
break;
}
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
hyjalAI* ai = CAST_AI(hyjalAI, creature->AI());
if (ai->EventBegun)
return false;
uint32 RageEncounter = ai->GetInstanceData(DATA_RAGEWINTERCHILLEVENT);
uint32 AnetheronEncounter = ai->GetInstanceData(DATA_ANETHERONEVENT);
if (RageEncounter == NOT_STARTED)
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEGIN_ALLY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
else if (RageEncounter == DONE && AnetheronEncounter == NOT_STARTED)
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_ANETHERON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
else if (RageEncounter == DONE && AnetheronEncounter == DONE)
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_RETREAT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
if (player->IsGameMaster())
AddGossipItemFor(player, GOSSIP_ICON_TRAINER, GOSSIP_ITEM_GM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
SendGossipMenuFor(player, 907, creature->GetGUID());
return true;
}
npc_hyjal_jaina() : CreatureScript("npc_hyjal_jaina") { }
CreatureAI* GetAI(Creature* creature) const override
{
if (!creature->GetInstanceScript())
return nullptr;
return new hyjalJainaAI(creature);
}
struct hyjalJainaAI : public ScriptedAI
{
hyjalJainaAI(Creature* creature) : ScriptedAI(creature) { }
hyjalAI* ai = new hyjalAI(creature);
void Reset() override
{
scheduler.CancelAll();
if (InstanceScript* hyjal = me->GetInstanceScript())
if (!hyjal->GetData(DATA_WAVE_STATUS))
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}
ai->Reset();
ai->EnterEvadeMode();
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_ATTACKED);
ai->Spells[0].SpellId = SPELL_BLIZZARD;
ai->Spells[0].Cooldown = urand(15000, 35000);
ai->Spells[0].TargetType = TARGETTYPE_RANDOM;
scheduler.Schedule(15s, 35s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
DoCast(target, SPELL_BLIZZARD);
context.Repeat();
}).Schedule(2s, 9s, [this](TaskContext context)
{
DoCastVictim(SPELL_PYROBLAST);
context.Repeat();
}).Schedule(15s, 45s, [this](TaskContext context)
{
DoCastSelf(SPELL_SUMMON_ELEMENTALS);
context.Repeat();
});
}
ai->Spells[1].SpellId = SPELL_PYROBLAST;
ai->Spells[1].Cooldown = urand(5500, 9500);
ai->Spells[1].TargetType = TARGETTYPE_RANDOM;
void IsSummonedBy(WorldObject* /*summoner*/) override
{
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
DoCastSelf(SPELL_SIMPLE_TELEPORT, true);
ai->Spells[2].SpellId = SPELL_SUMMON_ELEMENTALS;
ai->Spells[2].Cooldown = urand(15000, 45000);
ai->Spells[2].TargetType = TARGETTYPE_SELF;
// Should wait 2400ms
me->SetFacingTo(1.082104f);
DoCastSelf(SPELL_MASS_TELEPORT);
Talk(SAY_TELEPORT);
if (InstanceScript* hyjal = me->GetInstanceScript())
hyjal->SetData(DATA_HORDE_RETREAT, 0);
}
return ai;
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
if (InstanceScript* hyjal = me->GetInstanceScript())
hyjal->SetData(DATA_RESET_ALLIANCE, 0);
}
void PathEndReached(uint32 /*pathId*/) override
{
DoCastSelf(SPELL_MASS_TELEPORT);
Talk(SAY_TELEPORT);
if (InstanceScript* hyjal = me->GetInstanceScript())
hyjal->SetData(DATA_ALLIANCE_RETREAT, 0);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
scheduler.Update(diff);
}
};
bool OnGossipSelect(Player* /*player*/ , Creature* creature, uint32 /*sender*/, uint32 /*action*/) override
{
creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
if (InstanceScript* hyjal = creature->GetInstanceScript())
{
if (hyjal->GetBossState(DATA_WINTERCHILL) != DONE || hyjal->GetBossState(DATA_ANETHERON) != DONE)
{
hyjal->SetData(DATA_RESET_WAVES, 0);
hyjal->SetData(DATA_SPAWN_WAVES, 0);
}
else
{
creature->AI()->Talk(SAY_SUCCESS);
creature->GetMotionMaster()->MovePath(JAINA_RETREAT_PATH, false);
}
}
return true;
}
};
class npc_thrall : public CreatureScript
class npc_hyjal_thrall : public CreatureScript
{
public:
npc_thrall() : CreatureScript("npc_thrall") { }
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
hyjalAI* ai = CAST_AI(hyjalAI, creature->AI());
ai->DeSpawnVeins();//despawn the alliance veins
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1:
ai->StartEvent(player);
break;
case GOSSIP_ACTION_INFO_DEF + 2:
ai->FirstBossDead = true;
ai->WaveCount = 9;
ai->StartEvent(player);
break;
case GOSSIP_ACTION_INFO_DEF + 3:
ai->Retreat();
break;
case GOSSIP_ACTION_INFO_DEF:
ai->Debug = !ai->Debug;
//LOG_DEBUG("scripts", "HyjalAI - Debug mode has been toggled");
break;
}
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
{
hyjalAI* ai = CAST_AI(hyjalAI, creature->AI());
if (ai->EventBegun)
return false;
uint32 AnetheronEvent = ai->GetInstanceData(DATA_ANETHERONEVENT);
// Only let them start the Horde phases if Anetheron is dead.
if (AnetheronEvent == DONE && ai->GetInstanceData(DATA_ALLIANCE_RETREAT))
{
uint32 KazrogalEvent = ai->GetInstanceData(DATA_KAZROGALEVENT);
uint32 AzgalorEvent = ai->GetInstanceData(DATA_AZGALOREVENT);
if (KazrogalEvent == NOT_STARTED)
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEGIN_HORDE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
else if (KazrogalEvent == DONE && AzgalorEvent == NOT_STARTED)
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_AZGALOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
else if (AzgalorEvent == DONE)
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_RETREAT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
}
if (player->IsGameMaster())
AddGossipItemFor(player, GOSSIP_ICON_TRAINER, GOSSIP_ITEM_GM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
SendGossipMenuFor(player, 907, creature->GetGUID());
return true;
}
npc_hyjal_thrall() : CreatureScript("npc_hyjal_thrall") { }
CreatureAI* GetAI(Creature* creature) const override
{
if (!creature->GetInstanceScript())
return nullptr;
return new hyjalThrallAI(creature);
}
struct hyjalThrallAI : public ScriptedAI
{
hyjalThrallAI(Creature* creature) : ScriptedAI(creature) { }
hyjalAI* ai = new hyjalAI(creature);
void Reset() override
{
scheduler.CancelAll();
if (InstanceScript* hyjal = me->GetInstanceScript())
if (!hyjal->GetData(DATA_WAVE_STATUS))
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}
ai->Reset();
ai->EnterEvadeMode();
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_ATTACKED);
ai->Spells[0].SpellId = SPELL_CHAIN_LIGHTNING;
ai->Spells[0].Cooldown = urand(3000, 8000);
ai->Spells[0].TargetType = TARGETTYPE_VICTIM;
scheduler.Schedule(13s, 19s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
DoCast(target, SPELL_CHAIN_LIGHTNING);
context.Repeat();
}).Schedule(15s, 45s, [this](TaskContext context)
{
DoCastSelf(SPELL_FERAL_SPIRIT);
context.Repeat();
});
}
ai->Spells[1].SpellId = SPELL_SUMMON_DIRE_WOLF;
ai->Spells[1].Cooldown = urand(6000, 41000);
ai->Spells[1].TargetType = TARGETTYPE_RANDOM;
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
if (InstanceScript* hyjal = me->GetInstanceScript())
hyjal->SetData(DATA_RESET_HORDE, 0);
}
return ai;
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
scheduler.Update(diff);
}
};
bool OnGossipSelect(Player* /*player*/, Creature* creature, uint32 /*sender*/, uint32 /*action*/) override
{
creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
if (InstanceScript* hyjal = creature->GetInstanceScript())
{
if (hyjal->GetBossState(DATA_KAZROGAL) != DONE || hyjal->GetBossState(DATA_AZGALOR) != DONE)
{
hyjal->SetData(DATA_RESET_WAVES, 0);
hyjal->SetData(DATA_SPAWN_WAVES, 0);
}
else
{
creature->AI()->Talk(SAY_SUCCESS);
creature->SummonCreatureGroup(0);
}
}
return true;
}
};
class npc_tyrande_whisperwind : public CreatureScript
class npc_hyjal_tyrande : public CreatureScript
{
public:
npc_tyrande_whisperwind() : CreatureScript("npc_tyrande_whisperwind") { }
npc_hyjal_tyrande() : CreatureScript("npc_hyjal_tyrande") { }
CreatureAI* GetAI(Creature* creature) const override
{
if (!creature->GetInstanceScript())
return nullptr;
hyjalAI* ai = new hyjalAI(creature);
ai->Reset();
ai->EnterEvadeMode();
return ai;
return new hyjalTyrandeAI(creature);
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
struct hyjalTyrandeAI : public ScriptedAI
{
ClearGossipMenuFor(player);
if (action == GOSSIP_ACTION_INFO_DEF)
hyjalTyrandeAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
ItemPosCountVec dest;
uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, ITEM_TEAR_OF_GODDESS, 1);
if (msg == EQUIP_ERR_OK)
if (Item* item = player->StoreNewItem(dest, ITEM_TEAR_OF_GODDESS, true))
player->SendNewItem(item, 1, true, false, true);
SendGossipMenuFor(player, 907, creature->GetGUID());
scheduler.CancelAll();
}
return true;
}
bool OnGossipHello(Player* player, Creature* creature) override
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_ATTACKED);
scheduler.Schedule(60s, 70s, [this](TaskContext context)
{
DoCastVictim(SPELL_STARFALL);
context.Repeat();
}).Schedule(4s, [this](TaskContext context)
{
DoCastSelf(SPELL_TRUESHOT_AURA);
context.Repeat();
});
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
if (InstanceScript* hyjal = me->GetInstanceScript())
hyjal->SetData(DATA_RESET_NIGHT_ELF, 0);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
scheduler.Update(diff);
}
};
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 /*action*/) override
{
hyjalAI* ai = CAST_AI(hyjalAI, creature->AI());
uint32 AzgalorEvent = ai->GetInstanceData(DATA_AZGALOREVENT);
// Only let them get item if Azgalor is dead.
if (AzgalorEvent == DONE && !player->HasItemCount(ITEM_TEAR_OF_GODDESS))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ITEM_TYRANDE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
SendGossipMenuFor(player, 907, creature->GetGUID());
CloseGossipMenuFor(player);
creature->AI()->DoCast(player, SPELL_SUMMON_TEARS_OF_THE_GODDESS, true);
return true;
}
};
struct npc_hyjal_ground_trash : public ScriptedAI
{
npc_hyjal_ground_trash(Creature* creature) : ScriptedAI(creature)
{
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
scheduler.CancelAll();
}
void AttackStart(Unit* who) override
{
switch (me->GetEntry())
{
case NPC_NECRO:
case NPC_BANSH:
if (!who)
return;
if (me->Attack(who, true))
me->GetMotionMaster()->MoveChase(who, 30.0f);
break;
default:
ScriptedAI::AttackStart(who);
break;
}
}
void JustEngagedWith(Unit* /*who*/) override
{
switch (me->GetEntry())
{
case NPC_GHOUL:
{
scheduler.Schedule(3s, 7s, [this](TaskContext context)
{
DoCastSelf(SPELL_FRENZY);
context.Repeat(15s, 30s);
}).Schedule(1200ms, [this](TaskContext context)
{
if (me->GetHealthPct() <= 7)
DoCastSelf(SPELL_CANNIBALIZE);
else
context.Repeat();
});
break;
}
case NPC_CRYPT:
{
scheduler.Schedule(0s, 2400ms, [this](TaskContext context)
{
DoCastVictim(SPELL_CRYPT_SCARABS);
context.Repeat(2400ms, 8s);
});
break;
}
case NPC_ABOMI:
{
scheduler.Schedule(13s, 17s, [this](TaskContext context)
{
DoCastVictim(SPELL_KNOCKDOWN);
context.Repeat(16s, 25s);
});
break;
}
case NPC_NECRO:
{
scheduler.Schedule(0s, 2400ms, [this](TaskContext context)
{
DoCastVictim(SPELL_SHADOW_BOLT);
context.Repeat(2400ms, 4800ms);
}).Schedule(5s, 10s, [this](TaskContext context)
{
// TODO: Should target corpse, and skeletons should spawn at the target
switch (urand(1, 3))
{
case 1:
DoCastSelf(SPELL_RAISE_DEAD_1);
break;
case 2:
DoCastSelf(SPELL_RAISE_DEAD_2);
break;
case 3:
DoCastSelf(SPELL_RAISE_DEAD_3);
break;
}
context.Repeat(10s, 20s);
});
break;
}
case NPC_BANSH:
{
scheduler.Schedule(10s, 15s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
DoCast(target, SPELL_BANSHEE_CURSE);
context.Repeat(18s, 24s);
}).Schedule(5s, 15s, [this](TaskContext context)
{
DoCastSelf(SPELL_ANTI_MAGIC_SHELL);
context.Repeat(18s, 24s);
}).Schedule(0s, 1s, [this](TaskContext context)
{
DoCastVictim(SPELL_BANSHEE_WAIL);
context.Repeat(1800ms, 2200ms);
});
break;
}
case NPC_STALK:
{
scheduler.Schedule(3s, 6s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, PowerUsersSelector(me, Powers(POWER_MANA), 30.f, true)))
DoCast(target, SPELL_MANA_BURN);
context.Repeat(6s, 9s);
});
break;
}
}
}
void DoAction(int32 action) override
{
me->setActive(true);
switch (action)
{
case DATA_WINTERCHILL:
case DATA_ANETHERON:
case DATA_ALLIANCE_RETREAT:
me->GetMotionMaster()->MovePath(urand(ALLIANCE_BASE_CHARGE_1, ALLIANCE_BASE_CHARGE_3), false);
break;
case DATA_KAZROGAL:
case DATA_AZGALOR:
case DATA_HORDE_RETREAT:
me->GetMotionMaster()->MovePath(urand(HORDE_BASE_CHARGE_1, HORDE_BASE_CHARGE_3), false);
break;
case DATA_ARCHIMONDE:
me->GetMotionMaster()->MovePath(urand(NIGHT_ELF_BASE_CHARGE_1, NIGHT_ELF_BASE_CHARGE_3), false);
break;
}
}
void PathEndReached(uint32 pathId) override
{
// Delay is required because we are calling the movement generator from inside the pathing hook.
// If we issue another call here, it will be flushed before it is executed.
switch (pathId)
{
case ALLIANCE_BASE_CHARGE_1:
case ALLIANCE_BASE_CHARGE_2:
case ALLIANCE_BASE_CHARGE_3:
me->m_Events.AddEventAtOffset([this]()
{
me->GetMotionMaster()->MovePath(urand(ALLIANCE_BASE_PATROL_1, ALLIANCE_BASE_PATROL_3), true);
}, 1s);
break;
case HORDE_BASE_CHARGE_1:
case HORDE_BASE_CHARGE_2:
case HORDE_BASE_CHARGE_3:
me->m_Events.AddEventAtOffset([this]()
{
me->GetMotionMaster()->MovePath(urand(HORDE_BASE_PATROL_1, HORDE_BASE_PATROL_3), true);
}, 1s);
break;
case NIGHT_ELF_BASE_CHARGE_1:
case NIGHT_ELF_BASE_CHARGE_2:
case NIGHT_ELF_BASE_CHARGE_3:
me->m_Events.AddEventAtOffset([this]()
{
me->GetMotionMaster()->MoveRandom(5.f);
}, 1s);
break;
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
scheduler.Update(diff);
}
};
struct npc_hyjal_gargoyle : public ScriptedAI
{
npc_hyjal_gargoyle(Creature* creature) : ScriptedAI(creature)
{
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
scheduler.CancelAll();
}
void AttackStart(Unit* who) override
{
if (!who)
return;
if (me->Attack(who, true))
me->GetMotionMaster()->MoveChase(who, 25.0f);
}
void JustEngagedWith(Unit* /*who*/) override
{
scheduler.Schedule(0s, 2s, [this](TaskContext context)
{
DoCastVictim(SPELL_GARGOYLE_STRIKE);
context.Repeat(2s, 3s);
});
}
void DoAction(int32 action) override
{
me->setActive(true);
switch (action)
{
case DATA_ALLIANCE_RETREAT:
// TODO: Set up to attack NPC_BUILD
break;
case DATA_KAZROGAL:
case DATA_AZGALOR:
case DATA_HORDE_RETREAT:
if (me->GetPositionX() < 5500.f)
me->GetMotionMaster()->MovePath(urand(GARGOYLE_PATH_FORTRESS_1, GARGOYLE_PATH_FORTRESS_3), false);
else
me->GetMotionMaster()->MovePath(urand(GARGOYLE_PATH_TROLL_CAMP_1, GARGOYLE_PATH_TROLL_CAMP_3), false);
break;
default:
break;
}
}
void PathEndReached(uint32 /* pathId */) override
{
// TODO: Do they do something special after finishing the path?
me->m_Events.AddEventAtOffset([this]()
{
me->GetMotionMaster()->MoveRandom(30.f);
}, 1s);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
scheduler.Update(diff);
}
};
struct npc_hyjal_frost_wyrm : public ScriptedAI
{
npc_hyjal_frost_wyrm(Creature* creature) : ScriptedAI(creature)
{
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
scheduler.CancelAll();
}
void AttackStart(Unit* who) override
{
if (!who)
return;
if (me->Attack(who, true))
me->GetMotionMaster()->MoveChase(who, 25.0f);
}
void JustEngagedWith(Unit* /*who*/) override
{
scheduler.Schedule(0s, [this](TaskContext context)
{
DoCastVictim(SPELL_GARGOYLE_STRIKE);
context.Repeat(3500ms, 4s);
});
}
void DoAction(int32 action) override
{
me->setActive(true);
switch (action)
{
case DATA_KAZROGAL:
case DATA_AZGALOR:
case DATA_HORDE_RETREAT:
if (me->GetPositionX() < 5500.f)
me->GetMotionMaster()->MovePath(FROST_WYRM_FORTRESS, false);
else
me->GetMotionMaster()->MovePath(FROST_WYRM_TROLL_CAMP, false);
break;
default:
break;
}
}
void PathEndReached(uint32 pathId) override
{
if (pathId == FROST_WYRM_FORTRESS)
{
me->m_Events.AddEventAtOffset([this]()
{
me->GetMotionMaster()->MovePath(FROST_WYRM_FORTRESS_PATROL, true);
}, 1s);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
scheduler.Update(diff);
}
};
void AddSC_hyjal()
{
new npc_jaina_proudmoore();
new npc_thrall();
new npc_tyrande_whisperwind();
new npc_hyjal_jaina();
new npc_hyjal_thrall();
new npc_hyjal_tyrande();
RegisterHyjalAI(npc_hyjal_ground_trash);
RegisterHyjalAI(npc_hyjal_gargoyle);
RegisterHyjalAI(npc_hyjal_frost_wyrm);
}

View File

@@ -34,68 +34,148 @@ enum HyjalBosses
enum DataTypes
{
DATA_ANETHERON = 1,
DATA_ANETHERONEVENT = 2,
DATA_ARCHIMONDE = 3,
DATA_ARCHIMONDEEVENT = 4,
DATA_AZGALOR = 5,
DATA_AZGALOREVENT = 6,
DATA_JAINAPROUDMOORE = 7,
DATA_KAZROGAL = 8,
DATA_KAZROGALEVENT = 9,
DATA_RAGEWINTERCHILL = 10,
DATA_RAGEWINTERCHILLEVENT = 11,
DATA_THRALL = 12,
DATA_TYRANDEWHISPERWIND = 13,
DATA_TRASH = 14,
DATA_RESET_TRASH_COUNT = 15,
DATA_ALLIANCE_RETREAT = 16,
DATA_HORDE_RETREAT = 17,
DATA_RAIDDAMAGE = 18,
DATA_RESET_RAIDDAMAGE = 19,
TYPE_RETREAT = 20
DATA_WINTERCHILL = 1,
DATA_ANETHERON = 2,
DATA_KAZROGAL = 3,
DATA_AZGALOR = 4,
DATA_ARCHIMONDE = 5,
DATA_ALLIANCE_RETREAT = 11,
DATA_HORDE_RETREAT = 12,
DATA_JAINA = 13,
DATA_THRALL = 14,
DATA_TYRANDE = 15,
DATA_SPAWN_WAVES = 20,
DATA_SPAWN_INFERNALS = 21,
DATA_RESET_ALLIANCE = 22,
DATA_RESET_HORDE = 23,
DATA_RESET_NIGHT_ELF = 24,
DATA_RESET_WAVES = 25,
DATA_WAVE_STATUS = 26,
DATA_BOSS_WAVE = 27
};
enum WorldStateIds
enum HyjalWorldStateIds
{
WORLD_STATE_WAVES = 2842,
WORLD_STATE_ENEMY = 2453,
WORLD_STATE_ENEMYCOUNT = 2454
};
enum CreaturesIds
enum HyjalCreaturesIds
{
// Trash Mobs summoned in waves
NECROMANCER = 17899,
ABOMINATION = 17898,
GHOUL = 17895,
BANSHEE = 17905,
CRYPT_FIEND = 17897,
GARGOYLE = 17906,
FROST_WYRM = 17907,
GIANT_INFERNAL = 17908,
FEL_STALKER = 17916,
NPC_GHOUL = 17895,
NPC_CRYPT = 17897,
NPC_ABOMI = 17898,
NPC_NECRO = 17899,
NPC_BANSH = 17905,
NPC_GARGO = 17906,
NPC_FROST = 17907,
NPC_INFER = 17908,
NPC_STALK = 17916,
NPC_BUILD = 18304, // Serverside creature? Not found in CreateObject packets, but seen as targets
JAINA = 17772,
THRALL = 17852,
TYRANDE = 17948,
// Alliance Base
NPC_JAINA = 17772,
NPC_ALLIANCE_PEASANT = 17931,
NPC_ALLIANCE_KNIGHT = 17920,
NPC_ALLIANCE_FOOTMAN = 17919,
NPC_ALLIANCE_RIFLEMAN = 17921,
NPC_ALLIANCE_PRIEST = 17928,
NPC_ALLIANCE_SORCERESS = 17922,
// Bosses summoned after every 8 waves
RAGE_WINTERCHILL = 17767,
ANETHERON = 17808,
KAZROGAL = 17888,
AZGALOR = 17842,
ARCHIMONDE = 17968,
// Horde Base
NPC_THRALL = 17852,
NPC_HORDE_HEADHUNTER = 17934,
NPC_HORDE_SHAMAN = 17936,
NPC_HORDE_GRUNT = 17932,
NPC_HORDE_HEALING_WARD = 18036,
NPC_TAUREN_WARRIOR = 17933,
NPC_HORDE_WITCH_DOCTOR = 17935,
NPC_HORDE_PEON = 17937,
NPC_INFERNAL_RELAY = 18242,
NPC_INFERNAL_TARGET = 21075,
// Night Elf Base
NPC_TYRANDE = 17948,
NPC_DRUID_OF_THE_TALON = 3794,
NPC_DRUID_OF_THE_CLAW = 3795,
NPC_NELF_ANCIENT_PROT = 18487,
NPC_NELF_ANCIENT_OF_LORE = 18486,
NPC_NELF_ANCIENT_OF_WAR = 18485,
NPC_NELF_ARCHER = 17943,
NPC_NELF_HUNTRESS = 17945,
NPC_DRYAD = 17944,
// Bosses
NPC_WINTERCHILL = 17767,
NPC_ANETHERON = 17808,
NPC_KAZROGAL = 17888,
NPC_AZGALOR = 17842,
NPC_ARCHIMONDE = 17968,
NPC_WORLD_TRIGGER_TINY = 21987
};
enum GameobjectIds
enum HyjalGameobjectIds
{
GO_HORDE_ENCAMPMENT_PORTAL = 182060,
GO_NIGHT_ELF_VILLAGE_PORTAL = 182061,
GO_ANCIENT_GEM = 185557,
GO_ANCIENT_VEIN = 185557,
GO_ROARING_FLAME = 182592
GO_FLAME = 182260
};
enum HyjalMisc
{
MAX_WAVES_STANDARD = 9,
MAX_WAVES_RETREAT = 3,
MAX_WAVES_NIGHT_ELF = 1,
START_WAVE_WINTERCHILL = 0,
START_WAVE_ANETHERON = 9,
START_WAVE_KAZROGAL = 18,
START_WAVE_AZGALOR = 27,
START_WAVE_ALLIANCE_RETREAT = 36,
START_WAVE_HORDE_RETREAT = 39,
START_WAVE_NIGHT_ELF = 42,
CONTEXT_GROUP_WAVES = 1
};
enum HyjalPaths
{
ALLIANCE_BASE_CHARGE_1 = 177721,
ALLIANCE_BASE_CHARGE_2 = 177722,
ALLIANCE_BASE_CHARGE_3 = 177723,
ALLIANCE_BASE_PATROL_1 = 177724,
ALLIANCE_BASE_PATROL_2 = 177725,
ALLIANCE_BASE_PATROL_3 = 177726,
JAINA_RETREAT_PATH = 177727,
HORDE_BASE_CHARGE_1 = 178521,
HORDE_BASE_CHARGE_2 = 178522,
HORDE_BASE_CHARGE_3 = 178523,
HORDE_BASE_PATROL_1 = 178524,
HORDE_BASE_PATROL_2 = 178525,
HORDE_BASE_PATROL_3 = 178526,
NIGHT_ELF_BASE_CHARGE_1 = 179481,
NIGHT_ELF_BASE_CHARGE_2 = 179482,
NIGHT_ELF_BASE_CHARGE_3 = 179483,
GARGOYLE_PATH_TROLL_CAMP_1 = 179061,
GARGOYLE_PATH_TROLL_CAMP_2 = 179062,
GARGOYLE_PATH_TROLL_CAMP_3 = 179063,
GARGOYLE_PATH_FORTRESS_1 = 179064,
GARGOYLE_PATH_FORTRESS_2 = 179065,
GARGOYLE_PATH_FORTRESS_3 = 179066,
FROST_WYRM_TROLL_CAMP = 179071,
FROST_WYRM_FORTRESS = 179072,
FROST_WYRM_FORTRESS_PATROL = 179073,
HORDE_BOSS_PATH = 178527
};
template <class AI, class T>
@@ -104,4 +184,6 @@ inline AI* GetHyjalAI(T* obj)
return GetInstanceAI<AI>(obj, HyjalScriptName);
}
#define RegisterHyjalAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetHyjalAI)
#endif

View File

@@ -1,208 +0,0 @@
/*
* 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/>.
*/
#ifndef SC_HYJALAI_H
#define SC_HYJALAI_H
#include "ScriptedEscortAI.h"
#include "hyjal.h"
#define HYJAL_AI_MAX_SPELLS 3
enum SpellIds
{
SPELL_TELEPORT_VISUAL = 41232,
SPELL_MASS_TELEPORT = 16807,
//Spells for Jaina
SPELL_BRILLIANCE_AURA = 31260, // The database must handle this spell via creature_addon(it should, but is removed in evade..)
SPELL_BLIZZARD = 31266,
SPELL_PYROBLAST = 31263,
SPELL_SUMMON_ELEMENTALS = 31264,
//Thrall spells
SPELL_CHAIN_LIGHTNING = 31330,
SPELL_SUMMON_DIRE_WOLF = 31331
};
struct Wave
{
uint32 Mob[18]; // Stores Creature Entries to be summoned in Waves
uint32 WaveTimer; // The timer before the next wave is summoned
bool IsBoss; // Simply used to inform the wave summoner that the next wave contains a boss to halt all waves after that
};
const Wave AllianceWaves[] = // Waves that will be summoned in the Alliance Base
{
// Rage Winterchill Wave 1-8
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, 0, 0, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, 0, 0, 0, 0}, 180000, false},
// All 8 Waves are summoned, summon Rage Winterchill, next few waves are for Anetheron
{{RAGE_WINTERCHILL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, true},
// Anetheron Wave 1-8
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, 0, 0, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0}, 120000, false},
{{NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, BANSHEE, BANSHEE, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, NECROMANCER, NECROMANCER, BANSHEE, BANSHEE, BANSHEE, BANSHEE, 0, 0, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0}, 120000, false},
{{CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, BANSHEE, BANSHEE, BANSHEE, BANSHEE, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, GHOUL, GHOUL, 0, 0, 0, 0}, 120000, false},
{{GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, BANSHEE, BANSHEE, NECROMANCER, NECROMANCER, 0, 0, 0, 0}, 180000, false},
// All 8 Waves are summoned, summon Anatheron
{{ANETHERON, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, true}
};
const Wave HordeWaves[] = // Waves that are summoned in the Horde base
{
// Kaz'Rogal Wave 1-8
{{GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, BANSHEE, BANSHEE, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0}, 180000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, 0, 0, 0, 0}, 180000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, 0, 0, 0, 0}, 180000, false},
{{CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, NECROMANCER, NECROMANCER, 0, 0, 0, 0}, 180000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0}, 180000, false},
{{GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, FROST_WYRM, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 180000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, FROST_WYRM, 0, 0, 0, 0, 0, 0, 0}, 180000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, BANSHEE, BANSHEE, NECROMANCER, NECROMANCER, 0, 0}, 240000, false},
// All 8 Waves are summoned, summon Kaz'Rogal, next few waves are for Azgalor
{{KAZROGAL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, true},
// Azgalor Wave 1-8
{{ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0}, 180000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, FROST_WYRM, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, 0, 0, 0, 0}, 180000, false},
{{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, 0, 0, 0, 0}, 180000, false},
{{GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, 0, 0, 0, 0}, 180000, false},
{{FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0}, 180000, false},
{{NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, BANSHEE, BANSHEE, BANSHEE, BANSHEE, BANSHEE, BANSHEE, 0, 0, 0, 0, 0, 0}, 180000, false},
{{GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, FEL_STALKER, FEL_STALKER, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, 0, 0, 0, 0}, 180000, false},
{{CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, FEL_STALKER, FEL_STALKER, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, BANSHEE, BANSHEE, BANSHEE, BANSHEE, NECROMANCER, NECROMANCER, 0, 0}, 240000, false},
// All 8 Waves are summoned, summon Azgalor
{{AZGALOR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, true}
};
enum TargetType // Used in the spell cast system for the AI
{
TARGETTYPE_SELF = 0,
TARGETTYPE_RANDOM = 1,
TARGETTYPE_VICTIM = 2,
};
enum YellId
{
ATTACKED = 0, // Used when attacked and set in combat
BEGIN = 1, // Used when the event is begun
INCOMING = 2, // Used to warn the raid that another wave phase is coming
RALLY = 3, // Used to rally the raid and warn that the next wave has been summoned
FAILURE = 4, // Used when raid has failed (unsure where to place)
SUCCESS = 5, // Used when the raid has sucessfully defeated a wave phase
DEATH = 6, // Used on death
};
struct hyjalAI : public npc_escortAI
{
hyjalAI(Creature* creature);
void Reset() override; // Generically used to reset our variables. Do *not* call in EnterEvadeMode as this may make problems if the raid is still in combat
void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override; // Send creature back to spawn location and evade.
void JustEngagedWith(Unit* /*who*/) override; // Used to reset cooldowns for our spells and to inform the raid that we're under attack
void UpdateAI(uint32 diff) override; // Called to summon waves, check for boss deaths and to cast our spells.
void JustDied(Unit* /*killer*/) override; // Called on death, informs the raid that they have failed.
void SetFaction(uint32 _faction) // Set the faction to either Alliance or Horde in Hyjal
{
Faction = _faction;
}
void Retreat(); // "Teleport" (teleport visual + set invisible) all friendly creatures away from the base.
void SpawnVeins();
void DeSpawnVeins();
void JustSummoned(Creature* summoned) override;
void SummonedCreatureDespawn(Creature* summoned) override;
void HideNearPos(float x, float y);
void RespawnNearPos(float x, float y);
void WaypointReached(uint32 waypointId) override;
void DoOverrun(uint32 faction, const uint32 diff);
void MoveInLineOfSight(Unit* who) override;
void SummonCreature(uint32 entry, float Base[4][3]); // Summons a creature for that wave in that base
// Summons the next wave, calls SummonCreature
void SummonNextWave(const Wave wave[18], uint32 Count, float Base[4][3]);
void StartEvent(Player* player); // Begins the event by gossip click
uint32 GetInstanceData(uint32 Event); // Gets instance data for this instance, used to check if raid has gotten past a certain point and can access the next phase
public:
InstanceScript* instance;
ObjectGuid PlayerGUID;
ObjectGuid BossGUID[2];
ObjectGuid VeinGUID[14];
uint32 NextWaveTimer;
uint32 WaveCount;
uint32 CheckTimer;
uint32 Faction;
uint32 EnemyCount;
uint32 RetreatTimer;
bool EventBegun;
bool FirstBossDead;
bool SecondBossDead;
bool Summon;
bool bRetreat;
bool Debug;
bool VeinsSpawned[2];
uint8 InfernalCount;
SummonList Summons;
bool Overrun;
bool Teleported;
bool WaitForTeleport;
uint32 TeleportTimer;
uint32 OverrunCounter;
uint32 OverrunCounter2;
uint32 InfernalPoint;
uint32 RespawnTimer;
bool DoRespawn;
bool DoHide;
bool IsDummy;
uint32 MassTeleportTimer;
bool DoMassTeleport;
ObjectGuid DummyGuid;
struct Spell
{
uint32 SpellId;
uint32 Cooldown;
uint32 TargetType;
} Spells[HYJAL_AI_MAX_SPELLS];
private:
uint32 SpellTimer[3];
//GuidList CreatureList;
};
#endif

View File

@@ -1,51 +0,0 @@
/*
* 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/>.
*/
#ifndef SC_HYJAL_TRASH_AI_H
#define SC_HYJAL_TRASH_AI_H
#include "ScriptedEscortAI.h"
#include "hyjal.h"
#define MINRAIDDAMAGE 700000//minimal damage before trash can drop loot and reputation, resets if faction leader dies
struct hyjal_trashAI : public npc_escortAI
{
hyjal_trashAI(Creature* creature);
void UpdateAI(uint32 diff) override;
void JustDied(Unit* /*killer*/) override;
void DamageTaken(Unit* done_by, uint32& damage, DamageEffectType, SpellSchoolMask) override;
public:
InstanceScript* instance;
bool IsEvent;
uint32 Delay;
uint32 LastOverronPos;
bool IsOverrun;
bool SetupOverrun;
uint32 OverrunType;
uint8 faction;
bool useFlyPath;
uint32 damageTaken;
float DummyTarget[3];
//private:
};
#endif

View File

@@ -15,19 +15,12 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Instance_Mount_Hyjal
SD%Complete: 100
SDComment: Instance Data Scripts and functions to acquire mobs and set encounter status for use in various Hyjal Scripts
SDCategory: Caverns of Time, Mount Hyjal
EndScriptData */
#include "Chat.h"
#include "InstanceMapScript.h"
#include "InstanceScript.h"
#include "Opcodes.h"
#include "WorldPacket.h"
#include "hyjal_trash.h"
#include "hyjal.h"
/* Battle of Mount Hyjal encounters:
0 - Rage Winterchill event
@@ -37,7 +30,49 @@ EndScriptData */
4 - Archimonde event
*/
#define YELL_EFFORTS "All of your efforts have been in vain, for the draining of the World Tree has already begun. Soon the heart of your world will beat no more."
DoorData const doorData[] =
{
{ GO_HORDE_ENCAMPMENT_PORTAL, DATA_ANETHERON, DOOR_TYPE_PASSAGE },
{ GO_NIGHT_ELF_VILLAGE_PORTAL, DATA_AZGALOR, DOOR_TYPE_PASSAGE },
{ 0, 0, DOOR_TYPE_PASSAGE }
};
ObjectData const creatureData[] =
{
{ NPC_WINTERCHILL, DATA_WINTERCHILL },
{ NPC_ANETHERON, DATA_ANETHERON },
{ NPC_KAZROGAL, DATA_KAZROGAL },
{ NPC_AZGALOR, DATA_AZGALOR },
{ NPC_ARCHIMONDE, DATA_ARCHIMONDE },
{ NPC_THRALL, DATA_THRALL },
{ NPC_JAINA, DATA_JAINA },
{ NPC_TYRANDE, DATA_TYRANDE },
{ 0, 0 }
};
ObjectData const objectData[] =
{
{ 0, 0 }
};
Milliseconds hyjalWaveTimers[4][MAX_WAVES_STANDARD]
{
{ 130000ms, 130000ms, 130000ms, 130000ms, 130000ms, 130000ms, 130000ms, 190000ms, 0ms }, // Winterchill
{ 130000ms, 130000ms, 130000ms, 130000ms, 130000ms, 130000ms, 130000ms, 190000ms, 0ms }, // Anetheron
{ 130000ms, 155000ms, 130000ms, 155000ms, 130000ms, 130000ms, 155000ms, 225000ms, 0ms }, // Kaz'rogal
{ 130000ms, 190000ms, 190000ms, 190000ms, 130000ms, 155000ms, 190000ms, 225000ms, 0ms } // Azgalor
};
Milliseconds hyjalRetreatTimers[2][MAX_WAVES_RETREAT]
{
{ 10000ms, 6000ms , 0ms }, // Alliance
{ 10000ms, 40000ms, 0ms } // Horde
};
Milliseconds hyjalNightElfWaveTimers[1][MAX_WAVES_NIGHT_ELF]
{
{ 0ms }
};
class instance_hyjal : public InstanceMapScript
{
@@ -51,206 +86,383 @@ public:
struct instance_mount_hyjal_InstanceMapScript : public InstanceScript
{
instance_mount_hyjal_InstanceMapScript(Map* map) : InstanceScript(map) { }
instance_mount_hyjal_InstanceMapScript(Map* map) : InstanceScript(map)
{
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadDoorData(doorData);
LoadObjectData(creatureData, objectData);
}
void Initialize() override
{
SetHeaders(DataHeader);
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
m_uiAncientGemGUID.clear();
RaidDamage = 0;
Trash = 0;
hordeRetreat = 0;
allianceRetreat = 0;
ArchiYell = false;
}
bool IsEncounterInProgress() const override
{
for (uint8 i = 0; i < EncounterCount; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
return true;
return false;
_bossWave = 0;
_retreat = 0;
trash = 0;
_currentWave = 0;
_encounterNPCs.clear();
_baseAlliance.clear();
_baseHorde.clear();
_infernalTargets.clear();
_baseNightElf.clear();
_roaringFlameAlliance.clear();
_roaringFlameHorde.clear();
_ancientGemAlliance.clear();
_ancientGemHorde.clear();
}
void OnGameObjectCreate(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_HORDE_ENCAMPMENT_PORTAL:
HordeGate = go->GetGUID();
if (allianceRetreat)
HandleGameObject(ObjectGuid::Empty, true, go);
else
HandleGameObject(ObjectGuid::Empty, false, go);
break;
case GO_NIGHT_ELF_VILLAGE_PORTAL:
ElfGate = go->GetGUID();
if (hordeRetreat)
HandleGameObject(ObjectGuid::Empty, true, go);
else
HandleGameObject(ObjectGuid::Empty, false, go);
break;
case GO_ANCIENT_GEM:
m_uiAncientGemGUID.push_back(go->GetGUID());
if (go->GetPositionY() > -2500.f)
_ancientGemAlliance.insert(go->GetGUID());
else
_ancientGemHorde.insert(go->GetGUID());
go->DespawnOrUnsummon();
break;
case GO_FLAME:
if (go->GetPositionX() < 5360.f)
_roaringFlameAlliance.insert(go->GetGUID());
else
_roaringFlameHorde.insert(go->GetGUID());
go->DespawnOrUnsummon();
break;
}
InstanceScript::OnGameObjectCreate(go);
}
void OnCreatureCreate(Creature* creature) override
{
switch (creature->GetEntry())
{
case RAGE_WINTERCHILL:
RageWinterchill = creature->GetGUID();
// Alliance base
case NPC_ALLIANCE_PEASANT:
case NPC_ALLIANCE_KNIGHT:
case NPC_ALLIANCE_FOOTMAN:
case NPC_ALLIANCE_RIFLEMAN:
case NPC_ALLIANCE_PRIEST:
case NPC_ALLIANCE_SORCERESS:
case NPC_JAINA:
_baseAlliance.insert(creature->GetGUID());
break;
case ANETHERON:
Anetheron = creature->GetGUID();
// Horde base
case NPC_HORDE_HEADHUNTER:
case NPC_HORDE_SHAMAN:
case NPC_HORDE_GRUNT:
case NPC_HORDE_HEALING_WARD:
case NPC_TAUREN_WARRIOR:
case NPC_HORDE_WITCH_DOCTOR:
case NPC_HORDE_PEON:
case NPC_THRALL:
_baseHorde.insert(creature->GetGUID());
break;
case KAZROGAL:
Kazrogal = creature->GetGUID();
// Elf base
case NPC_DRUID_OF_THE_TALON:
case NPC_DRUID_OF_THE_CLAW:
case NPC_NELF_ANCIENT_PROT:
case NPC_NELF_ANCIENT_OF_LORE:
case NPC_NELF_ANCIENT_OF_WAR:
case NPC_NELF_ARCHER:
case NPC_NELF_HUNTRESS:
case NPC_DRYAD:
case NPC_TYRANDE:
_baseNightElf.insert(creature->GetGUID());
break;
case AZGALOR:
Azgalor = creature->GetGUID();
case NPC_INFERNAL_TARGET:
_infernalTargets.push_back(creature->GetGUID());
break;
case ARCHIMONDE:
Archimonde = creature->GetGUID();
break;
case JAINA:
JainaProudmoore = creature->GetGUID();
break;
case THRALL:
Thrall = creature->GetGUID();
break;
case TYRANDE:
TyrandeWhisperwind = creature->GetGUID();
case NPC_WINTERCHILL:
case NPC_ANETHERON:
case NPC_KAZROGAL:
case NPC_AZGALOR:
case NPC_NECRO:
case NPC_ABOMI:
case NPC_GHOUL:
case NPC_BANSH:
case NPC_CRYPT:
case NPC_STALK:
case NPC_GARGO:
case NPC_FROST:
case NPC_INFER:
if (_bossWave)
creature->AI()->DoAction(_bossWave);
else if (_retreat)
creature->AI()->DoAction(_retreat);
if (creature->IsSummon() && _bossWave)
{
DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, ++trash); // Update the instance wave count on new trash spawn
_encounterNPCs.insert(creature->GetGUID()); // Used for despawning on wipe
}
break;
}
InstanceScript::OnCreatureCreate(creature);
}
ObjectGuid GetGuidData(uint32 identifier) const override
void OnUnitDeath(Unit* unit) override
{
switch (identifier)
{
case DATA_RAGEWINTERCHILL:
return RageWinterchill;
case DATA_ANETHERON:
return Anetheron;
case DATA_KAZROGAL:
return Kazrogal;
case DATA_AZGALOR:
return Azgalor;
case DATA_ARCHIMONDE:
return Archimonde;
case DATA_JAINAPROUDMOORE:
return JainaProudmoore;
case DATA_THRALL:
return Thrall;
case DATA_TYRANDEWHISPERWIND:
return TyrandeWhisperwind;
}
InstanceScript::OnUnitDeath(unit);
return ObjectGuid::Empty;
if (Creature* creature = unit->ToCreature())
{
switch (creature->GetEntry())
{
case NPC_NECRO:
case NPC_ABOMI:
case NPC_GHOUL:
case NPC_BANSH:
case NPC_CRYPT:
case NPC_GARGO:
case NPC_FROST:
case NPC_INFER:
case NPC_STALK:
if (unit->ToCreature()->IsSummon())
{
if (_bossWave)
{
DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, --trash); // Update the instance wave count on new trash death
_encounterNPCs.erase(unit->ToCreature()->GetGUID()); // Used for despawning on wipe
if (trash == 0) // It can reach negatives if trash spawned after a retreat are killed, it shouldn't affect anything. Also happens on retail
SetData(DATA_SPAWN_WAVES, 1);
}
}
break;
case NPC_WINTERCHILL:
case NPC_ANETHERON:
case NPC_KAZROGAL:
case NPC_AZGALOR:
if (Creature* jaina = GetCreature(DATA_JAINA))
jaina->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
if (Creature* thrall = GetCreature(DATA_THRALL))
thrall->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
SetData(DATA_RESET_WAVES, 1);
break;
}
}
}
void SetData(uint32 type, uint32 data) override
{
switch (type)
{
case DATA_RAGEWINTERCHILLEVENT:
m_auiEncounter[0] = data;
break;
case DATA_ANETHERONEVENT:
m_auiEncounter[1] = data;
break;
case DATA_KAZROGALEVENT:
m_auiEncounter[2] = data;
break;
case DATA_AZGALOREVENT:
{
m_auiEncounter[3] = data;
if (data == DONE)
{
if (ArchiYell)
break;
ArchiYell = true;
Creature* creature = instance->GetCreature(Azgalor);
if (creature)
{
Creature* unit = creature->SummonCreature(NPC_WORLD_TRIGGER_TINY, creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000);
Map* map = creature->GetMap();
if (map->IsDungeon() && unit)
{
unit->SetVisible(false);
Map::PlayerList const& PlayerList = map->GetPlayers();
if (PlayerList.IsEmpty())
return;
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if (Player* player = i->GetSource())
{
WorldPacket packet;
ChatHandler::BuildChatPacket(packet, CHAT_MSG_MONSTER_YELL, LANG_UNIVERSAL, unit, player, YELL_EFFORTS);
player->SendDirectMessage(&packet);
player->PlayDirectSound(10986, player);
}
}
}
}
}
}
break;
case DATA_ARCHIMONDEEVENT:
m_auiEncounter[4] = data;
break;
case DATA_RESET_TRASH_COUNT:
Trash = 0;
break;
case DATA_TRASH:
if (data)
Trash = data;
else
Trash--;
DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, Trash);
break;
case TYPE_RETREAT:
if (data == SPECIAL)
{
if (!m_uiAncientGemGUID.empty())
{
for (ObjectGuid const& guid : m_uiAncientGemGUID)
{
//don't know how long it expected
DoRespawnGameObject(guid, DAY);
}
}
}
break;
case DATA_ALLIANCE_RETREAT:
allianceRetreat = data;
HandleGameObject(HordeGate, true);
_bossWave = 0;
_retreat = DATA_ALLIANCE_RETREAT;
// Spawn Ancient Gems
for (ObjectGuid const& guid : _ancientGemAlliance)
instance->GetGameObject(guid)->Respawn();
// Move all alliance NPCs near Jaina (only happens in this base, not Horde's)
if (Creature* jaina = GetCreature(DATA_JAINA))
{
for (ObjectGuid const& guid : _baseAlliance)
{
if (instance->GetCreature(guid) && instance->GetCreature(guid)->IsAlive())
{
float x, y, z;
jaina->GetNearPoint(instance->GetCreature(guid), x, y, z, 10.f, 0, jaina->GetAngle(instance->GetCreature(guid)));
instance->GetCreature(guid)->SetWalk(true);
instance->GetCreature(guid)->GetMotionMaster()->MovePoint(1, x, y, z);
}
}
}
// Despawn all alliance NPCs
_scheduler.Schedule(21000ms, [this](TaskContext)
{
for (ObjectGuid const& guid : _baseAlliance)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
// Spawn Roaring Flame after a delay
_scheduler.Schedule(30s, [this](TaskContext)
{
for (ObjectGuid const& guid : _roaringFlameAlliance)
instance->GetGameObject(guid)->Respawn();
});
});
// Spawn Overrun waves
ScheduleWaves(1ms, START_WAVE_ALLIANCE_RETREAT, MAX_WAVES_RETREAT, hyjalRetreatTimers[0]);
SaveToDB();
break;
case DATA_HORDE_RETREAT:
hordeRetreat = data;
HandleGameObject(ElfGate, true);
_bossWave = 0;
_retreat = DATA_HORDE_RETREAT;
for (ObjectGuid const& guid : _ancientGemHorde)
instance->GetGameObject(guid)->Respawn();
if (Creature* jaina = GetCreature(DATA_JAINA))
{
for (ObjectGuid const& guid : _baseHorde)
{
if (instance->GetCreature(guid) && instance->GetCreature(guid)->IsAlive())
{
float x, y, z;
jaina->GetNearPoint(instance->GetCreature(guid), x, y, z, 10.f, 0, jaina->GetAngle(instance->GetCreature(guid)));
instance->GetCreature(guid)->SetWalk(true);
instance->GetCreature(guid)->GetMotionMaster()->MovePoint(1, x, y, z);
}
}
}
_scheduler.Schedule(21000ms, [this](TaskContext)
{
for (ObjectGuid const& guid : _baseHorde)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
_scheduler.Schedule(30s, [this](TaskContext)
{
for (ObjectGuid const& guid : _roaringFlameHorde)
instance->GetGameObject(guid)->Respawn();
});
});
ScheduleWaves(1ms, START_WAVE_HORDE_RETREAT, MAX_WAVES_RETREAT, hyjalRetreatTimers[1]);
SaveToDB();
break;
case DATA_RAIDDAMAGE:
RaidDamage += data;
if (RaidDamage >= MINRAIDDAMAGE)
RaidDamage = MINRAIDDAMAGE;
case DATA_SPAWN_WAVES:
_retreat = 0;
if (GetBossState(DATA_WINTERCHILL) != DONE)
{
if (!_bossWave)
for (ObjectGuid const& guid : _baseAlliance)
if (Creature* creature = instance->GetCreature(guid))
creature->Respawn();
_bossWave = DATA_WINTERCHILL;
ScheduleWaves(1ms, START_WAVE_WINTERCHILL, MAX_WAVES_STANDARD, hyjalWaveTimers[DATA_WINTERCHILL - 1]);
}
else if (GetBossState(DATA_ANETHERON) != DONE)
{
if (!_bossWave)
for (ObjectGuid const& guid : _baseAlliance)
if (Creature* creature = instance->GetCreature(guid))
creature->Respawn();
_bossWave = DATA_ANETHERON;
ScheduleWaves(1ms, START_WAVE_ANETHERON, MAX_WAVES_STANDARD, hyjalWaveTimers[DATA_ANETHERON - 1]);
}
else if (GetBossState(DATA_KAZROGAL) != DONE)
{
if (!_bossWave)
for (ObjectGuid const& guid : _baseHorde)
if (Creature* creature = instance->GetCreature(guid))
creature->Respawn();
_bossWave = DATA_KAZROGAL;
ScheduleWaves(1ms, START_WAVE_KAZROGAL, MAX_WAVES_STANDARD, hyjalWaveTimers[DATA_KAZROGAL - 1]);
}
else if (GetBossState(DATA_AZGALOR) != DONE)
{
if (!_bossWave)
for (ObjectGuid const& guid : _baseHorde)
if (Creature* creature = instance->GetCreature(guid))
creature->Respawn();
_bossWave = DATA_AZGALOR;
ScheduleWaves(1ms, START_WAVE_AZGALOR, MAX_WAVES_STANDARD, hyjalWaveTimers[DATA_AZGALOR - 1]);
}
else if (GetBossState(DATA_ARCHIMONDE) != DONE)
{
_bossWave = DATA_ARCHIMONDE;
ScheduleWaves(1ms, START_WAVE_NIGHT_ELF, MAX_WAVES_NIGHT_ELF, hyjalNightElfWaveTimers[0]);
}
break;
case DATA_RESET_RAIDDAMAGE:
RaidDamage = 0;
case DATA_SPAWN_INFERNALS:
{
uint8 doubleInfernalCount = 2;
// Uses SmartAI
for (ObjectGuid const& guid : _infernalTargets)
{
if (Creature* target = instance->GetCreature(guid))
{
if (doubleInfernalCount > 0)
{
target->AI()->SetData(DATA_SPAWN_INFERNALS, 2); // Spawns 2 infernals, as there are only 6 spawns, some summon 2
doubleInfernalCount--;
}
else
target->AI()->SetData(DATA_SPAWN_INFERNALS, 1);
}
}
}
break;
case DATA_RESET_ALLIANCE:
for (ObjectGuid const& guid : _baseAlliance)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
for (ObjectGuid const& guid : _encounterNPCs)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
_scheduler.Schedule(300s, [this](TaskContext)
{
for (ObjectGuid const& guid : _baseAlliance)
if (Creature* creature = instance->GetCreature(guid))
creature->Respawn();
});
SetData(DATA_RESET_WAVES, 0);
break;
case DATA_RESET_HORDE:
for (ObjectGuid const& guid : _baseHorde)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
for (ObjectGuid const& guid : _encounterNPCs)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
_scheduler.Schedule(300s, [this](TaskContext)
{
for (ObjectGuid const& guid : _baseHorde)
if (Creature* creature = instance->GetCreature(guid))
creature->Respawn();
});
SetData(DATA_RESET_WAVES, 0);
break;
case DATA_RESET_NIGHT_ELF:
for (ObjectGuid const& guid : _baseNightElf)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
for (ObjectGuid const& guid : _encounterNPCs)
if (Creature* creature = instance->GetCreature(guid))
creature->DespawnOrUnsummon();
GetCreature(DATA_ARCHIMONDE)->DespawnOrUnsummon();
_scheduler.Schedule(300s, [this](TaskContext)
{
for (ObjectGuid const& guid : _baseNightElf)
if (Creature* creature = instance->GetCreature(guid))
creature->Respawn();
if (Creature* archi = GetCreature(DATA_ARCHIMONDE))
archi->Respawn();
});
SetData(DATA_RESET_WAVES, 0);
break;
case DATA_RESET_WAVES:
_scheduler.CancelGroup(CONTEXT_GROUP_WAVES);
_encounterNPCs.clear();
_currentWave = 0;
trash = 0;
_bossWave = 0;
_retreat = 0;
DoUpdateWorldState(WORLD_STATE_WAVES, _currentWave);
DoUpdateWorldState(WORLD_STATE_ENEMY, trash);
DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, trash);
break;
}
@@ -266,70 +478,75 @@ public:
{
switch (type)
{
case DATA_RAGEWINTERCHILLEVENT:
return m_auiEncounter[0];
case DATA_ANETHERONEVENT:
return m_auiEncounter[1];
case DATA_KAZROGALEVENT:
return m_auiEncounter[2];
case DATA_AZGALOREVENT:
return m_auiEncounter[3];
case DATA_ARCHIMONDEEVENT:
return m_auiEncounter[4];
case DATA_TRASH:
return Trash;
case DATA_ALLIANCE_RETREAT:
return allianceRetreat;
case DATA_HORDE_RETREAT:
return hordeRetreat;
case DATA_RAIDDAMAGE:
return RaidDamage;
case DATA_WAVE_STATUS:
return _currentWave;
break;
case DATA_BOSS_WAVE:
return _bossWave;
}
return 0;
}
void ReadSaveDataMore(std::istringstream& data) override
void ScheduleWaves(Milliseconds /* time */, uint8 startWaves, uint8 maxWaves, Milliseconds timerptr[])
{
data >> m_auiEncounter[0];
data >> m_auiEncounter[1];
data >> m_auiEncounter[2];
data >> m_auiEncounter[3];
data >> m_auiEncounter[4];
data >> allianceRetreat;
data >> hordeRetreat;
data >> RaidDamage;
// No overlapping!
_scheduler.CancelGroup(CONTEXT_GROUP_WAVES);
_scheduler.Schedule(1ms, [this, startWaves, maxWaves, timerptr](TaskContext context)
{
// If all waves reached, cancel scheduling new ones
if (_currentWave >= maxWaves)
return;
trash = 0; // Overrun event trash can modify the counter, so we set it to 0 at the start of every wave. World Update is sent in the first spawn
instance->SummonCreatureGroup(startWaves + _currentWave); // _currentWave should be 0 when this function is first called
// Check if it's time to summon Infernals
if (GetBossState(DATA_KAZROGAL) == DONE && GetBossState(DATA_AZGALOR) != DONE)
{
switch (_currentWave + 1)
{
case 3:
case 4:
case 7:
SetData(DATA_SPAWN_INFERNALS, 1);
break;
default:
break;
}
}
context.Repeat(timerptr[_currentWave]);
if (++_currentWave < maxWaves && _bossWave)
{
DoUpdateWorldState(WORLD_STATE_WAVES, _currentWave);
DoUpdateWorldState(WORLD_STATE_ENEMY, 1);
}
context.SetGroup(CONTEXT_GROUP_WAVES);
});
}
void WriteSaveDataMore(std::ostringstream& data) override
void Update(uint32 diff) override
{
data << m_auiEncounter[0] << ' '
<< m_auiEncounter[1] << ' '
<< m_auiEncounter[2] << ' '
<< m_auiEncounter[3] << ' '
<< m_auiEncounter[4]<< ' '
<< allianceRetreat << ' '
<< hordeRetreat << ' '
<< RaidDamage;
_scheduler.Update(diff);
}
protected:
uint32 m_auiEncounter[EncounterCount];
GuidList m_uiAncientGemGUID;
ObjectGuid RageWinterchill;
ObjectGuid Anetheron;
ObjectGuid Kazrogal;
ObjectGuid Azgalor;
ObjectGuid Archimonde;
ObjectGuid JainaProudmoore;
ObjectGuid Thrall;
ObjectGuid TyrandeWhisperwind;
ObjectGuid HordeGate;
ObjectGuid ElfGate;
uint32 Trash;
uint32 hordeRetreat;
uint32 allianceRetreat;
uint32 RaidDamage;
bool ArchiYell;
int32 trash;
uint8 _currentWave;
uint8 _bossWave;
uint8 _retreat;
TaskScheduler _scheduler;
GuidSet _encounterNPCs;
GuidSet _baseAlliance;
GuidSet _baseHorde;
GuidVector _infernalTargets;
GuidSet _baseNightElf;
GuidSet _ancientGemAlliance;
GuidSet _ancientGemHorde;
GuidSet _roaringFlameAlliance;
GuidSet _roaringFlameHorde;
};
};

View File

@@ -20,7 +20,6 @@ void AddSC_instance_blackfathom_deeps(); //Blackfathom Depths
void AddSC_hyjal(); //CoT Battle for Mt. Hyjal
void AddSC_boss_archimonde();
void AddSC_instance_mount_hyjal();
void AddSC_hyjal_trash();
void AddSC_boss_rage_winterchill();
void AddSC_boss_anetheron();
void AddSC_boss_kazrogal();
@@ -104,7 +103,6 @@ void AddKalimdorScripts()
AddSC_hyjal(); //CoT Battle for Mt. Hyjal
AddSC_boss_archimonde();
AddSC_instance_mount_hyjal();
AddSC_hyjal_trash();
AddSC_boss_rage_winterchill();
AddSC_boss_anetheron();
AddSC_boss_kazrogal();