Merge commit '26c583c24ab7dbbf1fecf3dcd737c1ad543c8b33' into Playerbot_1017

This commit is contained in:
Yunfan Li
2023-10-17 22:18:39 +08:00
57 changed files with 1461 additions and 879 deletions

View File

@@ -126,7 +126,7 @@ public:
for (std::unordered_multimap<uint32, Creature*>::const_iterator itr = creBounds.first; itr != creBounds.second;)
{
if (handler->GetSession())
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " ");
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, cInfo->Entry, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " ");
else
handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, itr->second->GetGUID().ToString().c_str(), itr->second->IsAlive() ? "*" : " ");
++itr;
@@ -138,7 +138,7 @@ public:
if (!liveFound)
{
if (handler->GetSession())
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name.c_str(), x, y, z, mapId, "", "");
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, cInfo->Entry, guid, cInfo->Name.c_str(), x, y, z, mapId, "", "");
else
handler->PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, guid, cInfo->Name.c_str(), x, y, z, mapId, "", "");
}

View File

@@ -15,13 +15,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
Name: npc_commandscript
%Complete: 100
Comment: All npc related commands
Category: commandscripts
EndScriptData */
#include "Chat.h"
#include "CreatureAI.h"
#include "CreatureGroups.h"
@@ -720,7 +713,7 @@ public:
if (!creatureTemplate)
continue;
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, creatureTemplate->Name.c_str(), x, y, z, mapId, "", "");
handler->PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, entry, guid, creatureTemplate->Name.c_str(), x, y, z, mapId, "", "");
++count;
} while (result->NextRow());

View File

@@ -24,8 +24,8 @@
enum Emotes
{
EMOTE_PHASE_PORTAL = 0,
EMOTE_PHASE_BANISH = 1
EMOTE_PHASE_BANISH = 0,
EMOTE_PHASE_PORTAL = 1
};
enum Spells
@@ -50,7 +50,7 @@ enum Portals
enum Groups
{
PORTAL_PHASE = 0,
VANISH_PHASE = 1
BANISH_PHASE = 1
};
const float PortalCoord[3][3] =
@@ -101,7 +101,6 @@ struct boss_netherspite : public BossAI
BossAI::Reset();
berserk = false;
HandleDoors(true);
DestroyPortals();
}
void SummonPortals()
@@ -121,23 +120,6 @@ struct boss_netherspite : public BossAI
}
}
void DestroyPortals()
{
for (int i = 0; i < 3; ++i)
{
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
{
portal->DisappearAndDie();
}
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
{
portal->DisappearAndDie();
}
PortalGUID[i].Clear();
BeamTarget[i].Clear();
}
}
void UpdatePortals() // Here we handle the beams' behavior
{
for (int j = 0; j < 3; ++j) // j = color
@@ -203,9 +185,14 @@ struct boss_netherspite : public BossAI
}
}
void SwitchToPortalPhase()
void SwitchToPortalPhase(bool aggro = false)
{
scheduler.CancelGroup(VANISH_PHASE);
if (!aggro)
{
Talk(EMOTE_PHASE_PORTAL);
}
scheduler.CancelGroup(BANISH_PHASE);
me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT);
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL);
SummonPortals();
@@ -230,27 +217,33 @@ struct boss_netherspite : public BossAI
DoCastRandomTarget(SPELL_VOIDZONE, 1, 45.0f, true, true);
context.Repeat(15s);
});
Talk(EMOTE_PHASE_PORTAL);
}
void SwitchToBanishPhase()
{
Talk(EMOTE_PHASE_BANISH);
scheduler.CancelGroup(PORTAL_PHASE);
me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA);
DoCastSelf(SPELL_BANISH_VISUAL, true);
DoCastSelf(SPELL_BANISH_ROOT, true);
DestroyPortals();
for (uint32 id : PortalID)
{
summons.DespawnEntry(id);
}
scheduler.Schedule(30s, [this](TaskContext)
{
SwitchToPortalPhase();
DoResetThreatList();
return;
}).Schedule(10s, VANISH_PHASE, [this](TaskContext context)
}).Schedule(10s, BANISH_PHASE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_NETHERBREATH, 0, 40.0f, true);
context.Repeat(5s, 7s);
});
Talk(EMOTE_PHASE_BANISH);
for (uint8 i = 0; i < 3; ++i)
{
me->RemoveAurasDueToSpell(NetherBuff[i]);
@@ -269,7 +262,7 @@ struct boss_netherspite : public BossAI
{
BossAI::JustEngagedWith(who);
HandleDoors(false);
SwitchToPortalPhase();
SwitchToPortalPhase(true);
DoZoneInCombat();
scheduler.Schedule(9min, [this](TaskContext /*context*/)
{
@@ -286,7 +279,6 @@ struct boss_netherspite : public BossAI
{
BossAI::JustDied(killer);
HandleDoors(true);
DestroyPortals();
}
void UpdateAI(uint32 diff) override

View File

@@ -205,13 +205,12 @@ struct boss_malchezaar : public BossAI
});
context.SetGroup(GROUP_ENFEEBLE);
scheduler.DelayGroup(GROUP_SHADOW_NOVA, 5s);
context.Repeat();
}).Schedule(35500ms, [this](TaskContext context)
{
DoCastAOE(SPELL_SHADOW_NOVA);
context.SetGroup(GROUP_SHADOW_NOVA);
context.Repeat();
context.Repeat(30s);
}).Schedule(40s, [this](TaskContext context)
{
if (!MaxSpawns(infernalTargets)) // only spawn infernal when the area is not full

View File

@@ -22,45 +22,52 @@
#include "karazhan.h"
#include "TaskScheduler.h"
enum ShadeOfAran
enum Texts
{
SAY_AGGRO = 0,
SAY_AGGRO = 0,
SAY_FLAMEWREATH = 1,
SAY_BLIZZARD = 2,
SAY_EXPLOSION = 3,
SAY_DRINK = 4,
SAY_ELEMENTALS = 5,
SAY_KILL = 6,
SAY_TIMEOVER = 7,
SAY_DEATH = 8,
SAY_BLIZZARD = 2,
SAY_EXPLOSION = 3,
SAY_DRINK = 4,
SAY_ELEMENTALS = 5,
SAY_KILL = 6,
SAY_TIMEOVER = 7,
SAY_DEATH = 8
};
enum Spells
{
//Spells
SPELL_FROSTBOLT = 29954,
SPELL_FIREBALL = 29953,
SPELL_ARCMISSLE = 29955,
SPELL_CHAINSOFICE = 29991,
SPELL_DRAGONSBREATH = 29964,
SPELL_MASSSLOW = 30035,
SPELL_FLAME_WREATH = 29946,
SPELL_AOE_CS = 29961,
SPELL_PLAYERPULL = 32265,
SPELL_AEXPLOSION = 29973,
SPELL_MASS_POLY = 29963,
SPELL_BLINK_CENTER = 29967,
SPELL_ELEMENTALS = 29962,
SPELL_CONJURE = 29975,
SPELL_DRINK = 30024,
SPELL_POTION = 32453,
SPELL_AOE_PYROBLAST = 29978,
SPELL_FROSTBOLT = 29954,
SPELL_FIREBALL = 29953,
SPELL_ARCMISSLE = 29955,
SPELL_CHAINSOFICE = 29991,
SPELL_DRAGONSBREATH = 29964,
SPELL_MASSSLOW = 30035,
SPELL_FLAME_WREATH = 29946,
SPELL_AOE_CS = 29961,
SPELL_PLAYERPULL = 32265,
SPELL_AEXPLOSION = 29973,
SPELL_MASS_POLY = 29963,
SPELL_BLINK_CENTER = 29967,
SPELL_CONJURE = 29975,
SPELL_DRINK = 30024,
SPELL_POTION = 32453,
SPELL_AOE_PYROBLAST = 29978,
//Creature Spells
SPELL_CIRCULAR_BLIZZARD = 29951,
SPELL_SHADOW_PYRO = 29978,
SPELL_SUMMON_WELEMENTAL_1 = 29962,
SPELL_SUMMON_WELEMENTAL_2 = 37051,
SPELL_SUMMON_WELEMENTAL_3 = 37052,
SPELL_SUMMON_WELEMENTAL_4 = 37053,
//Creatures
NPC_WATER_ELEMENTAL = 17167,
NPC_SHADOW_OF_ARAN = 18254,
NPC_ARAN_BLIZZARD = 17161,
SPELL_SUMMON_BLIZZARD = 29969, // Activates the Blizzard NPC
SPELL_SHADOW_PYRO = 29978
};
enum Creatures
{
NPC_SHADOW_OF_ARAN = 18254
};
enum SuperSpell
@@ -133,17 +140,11 @@ struct boss_shade_of_aran : public BossAI
ScheduleHealthCheckEvent(40, [&]{
Talk(SAY_ELEMENTALS);
for(Position pos : elementalPos)
std::vector<uint32> elementalSpells = { SPELL_SUMMON_WELEMENTAL_1, SPELL_SUMMON_WELEMENTAL_2, SPELL_SUMMON_WELEMENTAL_3, SPELL_SUMMON_WELEMENTAL_4 };
for (auto const& spell : elementalSpells)
{
if(Creature* elemental = me->SummonCreature(NPC_WATER_ELEMENTAL, pos, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000))
{
if(Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true))
{
DoStartNoMovement(target);
elemental->SetInCombatWithZone();
elemental->CombatStart(target);
}
}
DoCastAOE(spell, true);
}
});
}
@@ -255,7 +256,20 @@ struct boss_shade_of_aran : public BossAI
if (AvailableSpells)
{
CurrentNormalSpell = Spells[rand() % AvailableSpells];
DoCast(target, CurrentNormalSpell);
if (!me->CanCastSpell(CurrentNormalSpell))
{
me->SetWalk(false);
me->ResumeChasingVictim();
}
else
{
DoCast(target, CurrentNormalSpell);
if (me->GetVictim())
{
me->GetMotionMaster()->MoveChase(me->GetVictim(), 45.0f);
}
}
}
}
context.Repeat(2s);
@@ -341,12 +355,7 @@ struct boss_shade_of_aran : public BossAI
case SUPER_BLIZZARD:
Talk(SAY_BLIZZARD);
if (Creature* pSpawn = me->SummonCreature(NPC_ARAN_BLIZZARD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000))
{
pSpawn->SetFaction(me->GetFaction());
pSpawn->CastSpell(me, SPELL_CIRCULAR_BLIZZARD, false);
}
DoCastAOE(SPELL_SUMMON_BLIZZARD);
break;
}
}

View File

@@ -947,7 +947,7 @@ enum JulianneRomulo
SAY_ROMULO_AGGRO = 0,
SAY_ROMULO_DEATH = 1,
SAY_ROMULO_ENTER = 2,
SAY_ROMULO_DEATH2 = 2,
SAY_ROMULO_RESURRECT = 3,
SAY_ROMULO_SLAY = 4,
@@ -985,12 +985,10 @@ enum RAJGroups
enum RAJActions
{
ACTION_DIED_ANNOUNCE = 0,
ACTION_PHASE_SET = 1,
ACTION_FAKING_DEATH = 2,
ACTION_COMBAT_SCHEDULE = 3,
ACTION_DO_RESURRECT = 4,
ACTION_EARLY_REVIVE = 5,
//ACTION_DO_RESURRECT = 4,
//ACTION_RESS_ROMULO = 5,
ACTION_CANCEL_COMBAT = 6
};
@@ -999,7 +997,6 @@ void PretendToDie(Creature* creature)
creature->AI()->DoAction(ACTION_CANCEL_COMBAT);
creature->InterruptNonMeleeSpells(true);
creature->RemoveAllAuras();
creature->SetHealth(0);
creature->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
creature->SetReactState(REACT_PASSIVE);
creature->GetMotionMaster()->MovementExpired(false);
@@ -1032,14 +1029,6 @@ struct boss_julianne : public ScriptedAI
isFakingDeath = false;
}
InstanceScript* instance;
uint32 phase;
bool isFakingDeath;
bool summonedRomulo;
bool romuloDied;
void Reset() override
{
phase = PHASE_JULIANNE;
@@ -1051,10 +1040,8 @@ struct boss_julianne : public ScriptedAI
}
summonedRomulo = false;
romuloDied = false;
me->SetImmuneToPC(true);
//intro sequence
_scheduler.Schedule(1s, [this](TaskContext)
{
Talk(SAY_JULIANNE_ENTER);
@@ -1071,38 +1058,29 @@ struct boss_julianne : public ScriptedAI
{
switch(action)
{
case ACTION_DIED_ANNOUNCE:
romuloDied = true;
break;
case ACTION_EARLY_REVIVE:
romuloDied = true;
_resurrectScheduler.Schedule(10s, [this](TaskContext)
{
Talk(SAY_JULIANNE_RESURRECT);
romuloDied = false;
});
break;
case ACTION_PHASE_SET:
phase = PHASE_BOTH;
isFakingDeath = false;
break;
case ACTION_FAKING_DEATH:
isFakingDeath = false;
break;
case ACTION_COMBAT_SCHEDULE:
ScheduleCombat();
break;
case ACTION_DO_RESURRECT:
_resurrectScheduler.Schedule(1s, [this](TaskContext)
case ACTION_RESS_ROMULO:
me->m_Events.AddEventAtOffset([this]
{
if (Creature* Romulo = instance->GetCreature(DATA_ROMULO))
if (Creature* romulo = instance->GetCreature(DATA_ROMULO))
{
Talk(SAY_JULIANNE_RESURRECT);
Resurrect(Romulo);
Romulo->AI()->DoAction(ACTION_FAKING_DEATH);
romuloDied = false;
Resurrect(romulo);
romulo->AI()->DoAction(ACTION_FAKING_DEATH);
romulo->AI()->Talk(SAY_ROMULO_RESURRECT);
}
});
}, 1s);
break;
case ACTION_DO_RESURRECT:
phase = PHASE_BOTH;
isFakingDeath = false;
Resurrect(me);
me->ResumeChasingVictim();
break;
case ACTION_CANCEL_COMBAT:
_scheduler.CancelGroup(GROUP_COMBAT);
@@ -1128,11 +1106,11 @@ struct boss_julianne : public ScriptedAI
{
if (urand(0, 1) && summonedRomulo)
{
if (Creature* Romulo = instance->GetCreature(DATA_ROMULO))
if (Creature* romulo = instance->GetCreature(DATA_ROMULO))
{
if (Romulo->IsAlive() && !romuloDied)
if (!romulo->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
{
DoCast(Romulo, SPELL_ETERNAL_AFFECTION);
DoCast(romulo, SPELL_ETERNAL_AFFECTION);
}
}
}
@@ -1149,22 +1127,6 @@ struct boss_julianne : public ScriptedAI
ScheduleCombat();
}
void AttackStart(Unit* who) override
{
if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE))
return;
ScriptedAI::AttackStart(who);
}
void MoveInLineOfSight(Unit* who) override
{
if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE))
return;
ScriptedAI::MoveInLineOfSight(who);
}
void JustReachedHome() override
{
me->DespawnOrUnsummon();
@@ -1183,10 +1145,9 @@ struct boss_julianne : public ScriptedAI
phase = PHASE_ROMULO;
_scheduler.Schedule(10s, GROUP_RP, [this](TaskContext)
{
if (Creature* pRomulo = me->SummonCreature(CREATURE_ROMULO, ROMULO_X, ROMULO_Y, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR * 2 * IN_MILLISECONDS))
if (Creature* romulo = me->SummonCreature(CREATURE_ROMULO, ROMULO_X, ROMULO_Y, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR * 2 * IN_MILLISECONDS))
{
pRomulo->AI()->DoAction(ACTION_PHASE_SET);
pRomulo->SetInCombatWithZone();
romulo->SetInCombatWithZone();
}
summonedRomulo = true;
});
@@ -1201,13 +1162,12 @@ struct boss_julianne : public ScriptedAI
return;
}
//anything below only used if incoming damage will kill
damage = me->GetHealth() - 1;
if (phase == PHASE_JULIANNE)
{
damage = 0;
me->ClearTarget();
//this means already drinking, so return
if (isFakingDeath)
{
return;
@@ -1222,50 +1182,12 @@ struct boss_julianne : public ScriptedAI
return;
}
if (phase == PHASE_ROMULO)
if (phase == PHASE_BOTH && !isFakingDeath)
{
//LOG_ERROR("scripts", "boss_julianneAI: cannot take damage in PHASE_ROMULO, why was i here?");
damage = 0;
return;
PretendToDie(me);
isFakingDeath = true;
instance->DoAction(ACTION_SCHEDULE_RAJ_CHECK);
}
if (phase == PHASE_BOTH)
{
//if this is true then we have to kill romulo too
if (romuloDied)
{
if (Creature* Romulo = instance->GetCreature(DATA_ROMULO))
{
_scheduler.CancelAll();
_resurrectScheduler.CancelAll();
Romulo->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
Romulo->GetMotionMaster()->Clear();
Romulo->setDeathState(JUST_DIED);
Romulo->CombatStop(true);
Romulo->GetThreatMgr().ClearAllThreat();
Romulo->ReplaceAllDynamicFlags(UNIT_DYNFLAG_LOOTABLE);
//this does not seem to really work - the lootable dynamic flags
}
return;
}
//if not already returned, then romulo is alive and we can pretend die
if (Creature* Romulo = instance->GetCreature(DATA_ROMULO))
{
PretendToDie(me);
isFakingDeath = true;
Romulo->AI()->DoAction(ACTION_EARLY_REVIVE);
_scheduler.Schedule(10050ms, [this](TaskContext)
{
Resurrect(me);
isFakingDeath = false;
});
damage = 0;
return;
}
}
//LOG_ERROR("scripts", "boss_julianneAI: DamageTaken reach end of code, that should not happen.");
}
void EnterEvadeMode(EvadeReason reason) override
@@ -1286,15 +1208,17 @@ struct boss_julianne : public ScriptedAI
instance->SetBossState(DATA_OPERA_PERFORMANCE, DONE);
}
void KilledUnit(Unit* /*victim*/) override
void KilledUnit(Unit* victim) override
{
Talk(SAY_JULIANNE_SLAY);
if (victim != me)
{
Talk(SAY_JULIANNE_SLAY);
}
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
_resurrectScheduler.Update(diff);
if (!UpdateVictim())
{
@@ -1307,50 +1231,30 @@ struct boss_julianne : public ScriptedAI
}
}
private:
InstanceScript* instance;
uint32 phase;
bool isFakingDeath;
bool summonedRomulo;
TaskScheduler _scheduler;
TaskScheduler _resurrectScheduler;
};
struct boss_romulo : public ScriptedAI
{
boss_romulo(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript(); //not necessary
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
uint32 phase;
bool isFakingDeath;
bool julianneDead;
void Reset() override
{
phase = PHASE_ROMULO;
isFakingDeath = false;
julianneDead = false;
}
void DoAction(int32 action) override
{
switch(action)
{
case ACTION_DIED_ANNOUNCE:
julianneDead = true;
break;
case ACTION_EARLY_REVIVE:
julianneDead = true;
_resurrectScheduler.Schedule(10s, [this](TaskContext)
{
Talk(SAY_ROMULO_RESURRECT);
julianneDead = false;
});
break;
case ACTION_PHASE_SET:
phase = PHASE_ROMULO;
break;
case ACTION_FAKING_DEATH:
isFakingDeath = false;
break;
@@ -1375,9 +1279,11 @@ struct boss_romulo : public ScriptedAI
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (damage < me->GetHealth())
{
return;
}
//anything below only used if incoming damage will kill
damage = me->GetHealth() - 1;
if (phase == PHASE_ROMULO)
{
@@ -1386,63 +1292,24 @@ struct boss_romulo : public ScriptedAI
isFakingDeath = true;
phase = PHASE_BOTH;
if (Creature* Julianne = instance->GetCreature(DATA_JULIANNE))
me->m_Events.AddEventAtOffset([this]
{
Julianne->AI()->DoAction(ACTION_DIED_ANNOUNCE);
//resurrect julianne
_scheduler.Schedule(10s, GROUP_RP, [this](TaskContext)
Resurrect(me);
isFakingDeath = false;
if (Creature* julliane = instance->GetCreature(DATA_JULIANNE))
{
if (Creature* Julianne = instance->GetCreature(DATA_JULIANNE))
{
Resurrect(Julianne);
Julianne->AI()->DoAction(ACTION_PHASE_SET);
Julianne->AI()->DoAction(ACTION_DO_RESURRECT);
if (Julianne->GetVictim())
{
AttackStart(Julianne->GetVictim());
}
}
});
}
damage = 0;
return;
}
if (phase == PHASE_BOTH)
{
if (julianneDead)
{
if (Creature* Julianne = instance->GetCreature(DATA_JULIANNE))
{
_scheduler.CancelAll();
_resurrectScheduler.CancelAll();
Julianne->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
Julianne->GetMotionMaster()->Clear();
Julianne->setDeathState(JUST_DIED);
Julianne->CombatStop(true);
Julianne->GetThreatMgr().ClearAllThreat();
Julianne->ReplaceAllDynamicFlags(UNIT_DYNFLAG_LOOTABLE);
//this does not seem to really work
julliane->AI()->DoAction(ACTION_DO_RESURRECT);
}
return;
}
if (Creature* Julianne = instance->GetCreature(DATA_JULIANNE))
{
PretendToDie(me);
isFakingDeath = true;
Julianne->AI()->DoAction(ACTION_EARLY_REVIVE);
_scheduler.Schedule(10050ms, [this](TaskContext)
{
Resurrect(me);
isFakingDeath = false;
});
damage = 0;
return;
}
}, 3s);
}
if (phase == PHASE_BOTH && !isFakingDeath)
{
Talk(SAY_ROMULO_DEATH2);
PretendToDie(me);
instance->DoAction(ACTION_SCHEDULE_RAJ_CHECK);
isFakingDeath = true;
}
//LOG_ERROR("scripts", "boss_romuloAI: DamageTaken reach end of code, that should not happen.");
}
void ScheduleCombat()
@@ -1488,14 +1355,6 @@ struct boss_romulo : public ScriptedAI
ScheduleCombat();
}
void MoveInLineOfSight(Unit* who) override
{
if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE))
return;
ScriptedAI::MoveInLineOfSight(who);
}
void EnterEvadeMode(EvadeReason reason) override
{
ScriptedAI::EnterEvadeMode(reason);
@@ -1510,15 +1369,17 @@ struct boss_romulo : public ScriptedAI
instance->SetBossState(DATA_OPERA_PERFORMANCE, DONE);
}
void KilledUnit(Unit* /*victim*/) override
void KilledUnit(Unit* victim) override
{
Talk(SAY_ROMULO_SLAY);
if (victim != me)
{
Talk(SAY_ROMULO_SLAY);
}
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
_resurrectScheduler.Update(diff);
if (!UpdateVictim())
{
@@ -1531,8 +1392,10 @@ struct boss_romulo : public ScriptedAI
}
}
private:
InstanceScript* instance;
uint32 phase;
bool isFakingDeath;
TaskScheduler _scheduler;
TaskScheduler _resurrectScheduler;
};
void AddSC_bosses_opera()

View File

@@ -240,6 +240,9 @@ public:
}
}
break;
case DONE:
HandleGameObject(m_uiGamesmansExitDoor, true);
break;
}
default:
DoRemoveAurasDueToSpellOnPlayers(SPELL_GAME_IN_SESSION);
@@ -418,6 +421,42 @@ public:
return 0;
}
void DoAction(int32 actionId) override
{
if (actionId == ACTION_SCHEDULE_RAJ_CHECK)
{
scheduler.Schedule(10s, [this](TaskContext)
{
Creature* julliane = GetCreature(DATA_JULIANNE);
Creature* romulo = GetCreature(DATA_ROMULO);
if (julliane && romulo)
{
if (julliane->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)
&& romulo->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
{
julliane->KillSelf();
julliane->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
romulo->KillSelf();
romulo->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
else
{
if (romulo->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
{
julliane->AI()->DoAction(ACTION_RESS_ROMULO);
}
if (julliane->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
{
julliane->AI()->DoAction(ACTION_DO_RESURRECT);
}
}
}
});
}
}
ObjectGuid GetGuidData(uint32 data) const override
{
switch (data)

View File

@@ -204,6 +204,14 @@ enum KarazhanChessGameFactions
CHESS_FACTION_BOTH = 536
};
enum InstanceActions
{
ACTION_SCHEDULE_RAJ_CHECK,
ACTION_DO_RESURRECT = 4,
ACTION_RESS_ROMULO = 5,
};
template <class AI, class T>
inline AI* GetKarazhanAI(T* obj)
{

View File

@@ -105,7 +105,7 @@ public:
events.Reset();
summons.DespawnAll();
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, false);
instance->SetData(DATA_KAELTHAS_EVENT, NOT_STARTED);
instance->SetBossState(DATA_KAELTHAS, NOT_STARTED);
me->SetImmuneToAll(false);
}
@@ -125,12 +125,17 @@ public:
void JustDied(Unit*) override
{
instance->SetData(DATA_KAELTHAS_EVENT, DONE);
instance->SetBossState(DATA_KAELTHAS, DONE);
if (GameObject* orb = instance->GetGameObject(DATA_ESCAPE_ORB))
{
orb->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
instance->SetData(DATA_KAELTHAS_EVENT, IN_PROGRESS);
instance->SetBossState(DATA_KAELTHAS, IN_PROGRESS);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_SPELL_FIREBALL, 0);

View File

@@ -105,7 +105,7 @@ public:
{
PlayersKilled = SAY_PLAYER_KILLED;
HelpersKilled = SAY_HELPER_DIED;
instance->SetData(DATA_DELRISSA_EVENT, NOT_STARTED);
instance->SetBossState(DATA_DELRISSA, NOT_STARTED);
summons.Respawn();
me->SetLootMode(0);
@@ -141,7 +141,7 @@ public:
{
me->loot.clear();
me->loot.FillLoot(me->GetCreatureTemplate()->lootid, LootTemplates_Creature, me->GetLootRecipient(), false, false, 1, me);
instance->SetData(DATA_DELRISSA_EVENT, DONE);
instance->SetBossState(DATA_DELRISSA, DONE);
me->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
}
++HelpersKilled;
@@ -151,7 +151,7 @@ public:
{
Talk(SAY_AGGRO);
summons.DoZoneInCombat();
instance->SetData(DATA_DELRISSA_EVENT, IN_PROGRESS);
instance->SetBossState(DATA_DELRISSA, IN_PROGRESS);
events.ScheduleEvent(EVENT_SPELL_FLASH_HEAL, 15000);
events.ScheduleEvent(EVENT_SPELL_RENEW, 10000);
@@ -177,7 +177,7 @@ public:
Talk(SAY_DEATH);
if (HelpersKilled == MAX_ACTIVE_HELPERS + 1)
instance->SetData(DATA_DELRISSA_EVENT, DONE);
instance->SetBossState(DATA_DELRISSA, DONE);
}
void UpdateAI(uint32 diff) override

View File

@@ -106,7 +106,7 @@ public:
events.Reset();
summons.DespawnAll();
SpawnCrystals();
instance->SetData(DATA_SELIN_EVENT, NOT_STARTED);
instance->SetBossState(DATA_SELIN_FIREHEART, NOT_STARTED);
CrystalGUID.Clear();
me->SetPower(POWER_MANA, 0);
}
@@ -114,7 +114,7 @@ public:
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
instance->SetData(DATA_SELIN_EVENT, IN_PROGRESS);
instance->SetBossState(DATA_SELIN_FIREHEART, IN_PROGRESS);
events.ScheduleEvent(EVENT_SPELL_DRAIN_LIFE, 2500, 1);
events.ScheduleEvent(EVENT_SPELL_FEL_EXPLOSION, 2000);
@@ -134,7 +134,7 @@ public:
{
Talk(SAY_DEATH);
instance->SetData(DATA_SELIN_EVENT, DONE); // Encounter complete!
instance->SetBossState(DATA_SELIN_FIREHEART, DONE); // Encounter complete!
summons.DespawnAll();
}

View File

@@ -87,7 +87,7 @@ public:
summons.DespawnAll();
IntervalHealthAmount = 1;
instance->SetData(DATA_VEXALLUS_EVENT, NOT_STARTED);
instance->SetBossState(DATA_VEXALLUS, NOT_STARTED);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ENERGY_FEEDBACK);
}
@@ -100,14 +100,14 @@ public:
void JustDied(Unit* /*killer*/) override
{
summons.DespawnAll();
instance->SetData(DATA_VEXALLUS_EVENT, DONE);
instance->SetBossState(DATA_VEXALLUS, DONE);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_ENERGY_FEEDBACK);
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
instance->SetData(DATA_VEXALLUS_EVENT, IN_PROGRESS);
instance->SetBossState(DATA_VEXALLUS, IN_PROGRESS);
events.ScheduleEvent(EVENT_SPELL_CHAIN_LIGHTNING, 8000);
events.ScheduleEvent(EVENT_SPELL_ARCANE_SHOCK, 5000);

View File

@@ -26,6 +26,21 @@ ObjectData const creatureData[] =
{ 0, 0 }
};
ObjectData const gameobjectData[] =
{
{ GO_ESCAPE_ORB, DATA_ESCAPE_ORB },
{ 0, 0, }
};
DoorData const doorData[] =
{
{ GO_SELIN_DOOR, DATA_SELIN_FIREHEART, DOOR_TYPE_PASSAGE },
{ GO_SELIN_ENCOUNTER_DOOR, DATA_SELIN_FIREHEART, DOOR_TYPE_ROOM },
{ GO_VEXALLUS_DOOR, DATA_VEXALLUS, DOOR_TYPE_PASSAGE },
{ GO_DELRISSA_DOOR, DATA_DELRISSA, DOOR_TYPE_PASSAGE },
{ 0, 0, DOOR_TYPE_ROOM } // END
};
Position const KalecgosSpawnPos = { 164.3747f, -397.1197f, 2.151798f, 1.66219f };
class instance_magisters_terrace : public InstanceMapScript
@@ -38,47 +53,16 @@ public:
instance_magisters_terrace_InstanceMapScript(Map* map) : InstanceScript(map)
{
SetHeaders(DataHeader);
LoadObjectData(creatureData, nullptr);
SetBossNumber(MAX_ENCOUNTER);
LoadObjectData(creatureData, gameobjectData);
LoadDoorData(doorData);
}
uint32 Encounter[MAX_ENCOUNTER];
ObjectGuid VexallusDoorGUID;
ObjectGuid SelinDoorGUID;
ObjectGuid SelinEncounterDoorGUID;
ObjectGuid DelrissaDoorGUID;
ObjectGuid KaelDoorGUID;
ObjectGuid EscapeOrbGUID;
ObjectGuid DelrissaGUID;
ObjectGuid KaelGUID;
void Initialize() override
{
memset(&Encounter, 0, sizeof(Encounter));
}
bool IsEncounterInProgress() const override
{
for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (Encounter[i] == IN_PROGRESS)
return true;
return false;
}
uint32 GetData(uint32 identifier) const override
{
switch (identifier)
{
case DATA_SELIN_EVENT:
case DATA_VEXALLUS_EVENT:
case DATA_DELRISSA_EVENT:
case DATA_KAELTHAS_EVENT:
return Encounter[identifier];
}
return 0;
}
void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) override
{
if (eventId == EVENT_SPAWN_KALECGOS)
@@ -97,37 +81,6 @@ public:
}
}
void SetData(uint32 identifier, uint32 data) override
{
switch (identifier)
{
case DATA_SELIN_EVENT:
HandleGameObject(SelinDoorGUID, data == DONE);
HandleGameObject(SelinEncounterDoorGUID, data != IN_PROGRESS);
Encounter[identifier] = data;
break;
case DATA_VEXALLUS_EVENT:
if (data == DONE)
HandleGameObject(VexallusDoorGUID, true);
Encounter[identifier] = data;
break;
case DATA_DELRISSA_EVENT:
if (data == DONE)
HandleGameObject(DelrissaDoorGUID, true);
Encounter[identifier] = data;
break;
case DATA_KAELTHAS_EVENT:
HandleGameObject(KaelDoorGUID, data != IN_PROGRESS);
if (data == DONE)
if (GameObject* escapeOrb = instance->GetGameObject(EscapeOrbGUID))
escapeOrb->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
Encounter[identifier] = data;
break;
}
SaveToDB();
}
void OnCreatureCreate(Creature* creature) override
{
switch (creature->GetEntry())
@@ -148,60 +101,11 @@ public:
InstanceScript::OnCreatureCreate(creature);
}
void OnGameObjectCreate(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_SELIN_DOOR:
if (GetData(DATA_SELIN_EVENT) == DONE)
HandleGameObject(ObjectGuid::Empty, true, go);
SelinDoorGUID = go->GetGUID();
break;
case GO_SELIN_ENCOUNTER_DOOR:
SelinEncounterDoorGUID = go->GetGUID();
break;
case GO_VEXALLUS_DOOR:
if (GetData(DATA_VEXALLUS_EVENT) == DONE)
HandleGameObject(ObjectGuid::Empty, true, go);
VexallusDoorGUID = go->GetGUID();
break;
case GO_DELRISSA_DOOR:
if (GetData(DATA_DELRISSA_EVENT) == DONE)
HandleGameObject(ObjectGuid::Empty, true, go);
DelrissaDoorGUID = go->GetGUID();
break;
case GO_KAEL_DOOR:
KaelDoorGUID = go->GetGUID();
break;
case GO_ESCAPE_ORB:
if (GetData(DATA_KAELTHAS_EVENT) == DONE)
go->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
EscapeOrbGUID = go->GetGUID();
break;
}
}
// @todo: Use BossStates. This is for code compatibility
void ReadSaveDataMore(std::istringstream& data) override
{
data >> Encounter[1];
data >> Encounter[2];
data >> Encounter[3];
}
void WriteSaveDataMore(std::ostringstream& data) override
{
data << Encounter[0] << ' ' << Encounter[1] << ' ' << Encounter[2] << ' ' << Encounter[3];
}
ObjectGuid GetGuidData(uint32 identifier) const override
{
switch (identifier)
if (identifier == NPC_DELRISSA)
{
case NPC_DELRISSA:
return DelrissaGUID;
return DelrissaGUID;
}
return ObjectGuid::Empty;

View File

@@ -28,13 +28,14 @@
enum MTData
{
DATA_SELIN_EVENT = 0,
DATA_VEXALLUS_EVENT = 1,
DATA_DELRISSA_EVENT = 2,
DATA_KAELTHAS_EVENT = 3,
DATA_SELIN_FIREHEART = 0,
DATA_VEXALLUS = 1,
DATA_DELRISSA = 2,
DATA_KAELTHAS = 3,
MAX_ENCOUNTER = 4,
DATA_KALECGOS = 5
DATA_KALECGOS = 5,
DATA_ESCAPE_ORB = 6
};
enum MTCreatures

View File

@@ -1031,35 +1031,32 @@ class spell_brewfest_apple_trap : public SpellScript
}
};
class spell_q11117_catch_the_wild_wolpertinger : public SpellScript
enum Catch
{
PrepareSpellScript(spell_q11117_catch_the_wild_wolpertinger);
NPC_WILD_WOLPERTINGER = 23487,
SpellCastResult CheckTarget()
ITEM_STUNNED_WOLPERTINGER = 32906
};
class spell_catch_the_wild_wolpertinger : public AuraScript
{
PrepareAuraScript(spell_catch_the_wild_wolpertinger);
void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (Unit* caster = GetCaster())
if (caster->ToPlayer())
if (Unit* target = caster->ToPlayer()->GetSelectedUnit())
if (target->GetEntry() == 23487 && target->IsAlive())
return SPELL_CAST_OK;
return SPELL_FAILED_BAD_TARGETS;
}
void HandleDummyEffect(SpellEffIndex /*effIndex*/)
{
if (GetCaster() && GetCaster()->ToPlayer())
if (Creature* wild = GetTarget()->ToCreature())
{
GetCaster()->ToPlayer()->AddItem(32906, 1);
if (Unit* target = GetCaster()->ToPlayer()->GetSelectedUnit())
target->ToCreature()->DespawnOrUnsummon(500);
if (wild->GetEntry() == NPC_WILD_WOLPERTINGER)
{
wild->ToCreature()->DespawnOrUnsummon(1s, 0s);
GetCaster()->ToPlayer()->AddItem(ITEM_STUNNED_WOLPERTINGER, 1);
}
}
}
void Register() override
{
OnCheckCast += SpellCheckCastFn(spell_q11117_catch_the_wild_wolpertinger::CheckTarget);
OnEffectHitTarget += SpellEffectFn(spell_q11117_catch_the_wild_wolpertinger::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
OnEffectApply += AuraEffectApplyFn(spell_catch_the_wild_wolpertinger::HandleEffectApply, EFFECT_0, SPELL_AURA_MOD_PACIFY_SILENCE, AURA_EFFECT_HANDLE_REAL);
}
};
@@ -2072,7 +2069,7 @@ void AddSC_event_brewfest_scripts()
RegisterSpellScript(spell_brewfest_ram_fatigue);
RegisterSpellScript(spell_brewfest_apple_trap);
// other
RegisterSpellScript(spell_q11117_catch_the_wild_wolpertinger);
RegisterSpellScript(spell_catch_the_wild_wolpertinger);
RegisterSpellScript(spell_brewfest_fill_keg);
RegisterSpellScript(spell_brewfest_unfill_keg);
RegisterSpellScript(spell_brewfest_toss_mug);

View File

@@ -253,6 +253,39 @@ class spell_midsummer_ribbon_pole : public AuraScript
}
};
class spell_midsummer_ribbon_pole_visual : public SpellScript
{
PrepareSpellScript(spell_midsummer_ribbon_pole_visual)
void UpdateTarget(WorldObject*& target)
{
if (!target)
return;
// find NPC at ribbon pole top as target
// trap 181604 also spawns NPCs at pole bottom - ignore those
std::list<Creature*> crList;
target->GetCreaturesWithEntryInRange(crList, 30.0f, NPC_RIBBON_POLE_DEBUG_TARGET);
if (crList.empty())
return;
for (std::list<Creature*>::const_iterator itr = crList.begin(); itr != crList.end(); ++itr)
{
// NPC on ribbon pole top is no tempsummon
if (!(*itr)->ToTempSummon())
{
target = *itr;
return;
}
}
}
void Register() override
{
OnObjectTargetSelect += SpellObjectTargetSelectFn(spell_midsummer_ribbon_pole_visual::UpdateTarget, EFFECT_0, TARGET_UNIT_NEARBY_ENTRY);
}
};
class spell_midsummer_torch_quest : public AuraScript
{
PrepareAuraScript(spell_midsummer_torch_quest)
@@ -296,6 +329,7 @@ enum flingTorch
SPELL_FLING_TORCH_DUMMY = 46747,
SPELL_MISSED_TORCH = 45676,
SPELL_TORCH_COUNTER = 45693,
SPELL_TORCH_SHADOW = 46105
};
class spell_midsummer_fling_torch : public SpellScript
@@ -339,7 +373,10 @@ class spell_midsummer_fling_torch : public SpellScript
// we have any pos
if (pos.GetPositionX())
{
caster->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), SPELL_FLING_TORCH, true);
caster->CastSpell(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), SPELL_TORCH_SHADOW, true);
}
}
void HandleFinish()
@@ -417,15 +454,18 @@ enum eJuggle
SPELL_TORCH_CHECK = 45644,
SPELL_GIVE_TORCH = 45280,
QUEST_TORCH_CATCHING_A = 11657,
QUEST_TORCH_CATCHING_H = 11923
QUEST_TORCH_CATCHING_H = 11923,
SPELL_TORCH_SHADOW_SELF = 46121,
SPELL_TORCH_SHADOW_SLOW = 46120,
SPELL_TORCH_SHADOW_MED = 46118,
SPELL_TORCH_SHADOW_FAST = 46117
};
class spell_midsummer_juggling_torch : public SpellScript
{
PrepareSpellScript(spell_midsummer_juggling_torch);
bool handled;
bool Load() override { handled = false; return true; }
void HandleFinish()
{
Unit* caster = GetCaster();
@@ -435,39 +475,36 @@ class spell_midsummer_juggling_torch : public SpellScript
if (const WorldLocation* loc = GetExplTargetDest())
{
if (loc->GetExactDist(caster) < 3.0f)
{
caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_JUGGLE_SELF, true);
caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_TORCH_SHADOW_SELF, true);
}
else if (loc->GetExactDist(caster) < 10.0f)
{
caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_JUGGLE_SLOW, true);
caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_TORCH_SHADOW_SLOW, true);
}
else if (loc->GetExactDist(caster) < 25.0f)
{
caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_JUGGLE_MED, true);
caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_TORCH_SHADOW_MED, true);
}
else
{
caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_JUGGLE_FAST, true);
caster->CastSpell(loc->GetPositionX(), loc->GetPositionY(), loc->GetPositionZ(), SPELL_TORCH_SHADOW_FAST, true);
}
}
else
{
caster->CastSpell(caster, SPELL_JUGGLE_SELF, true);
}
void HandleDummy(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
Unit* caster = GetCaster();
if (!caster || caster->GetTypeId() != TYPEID_PLAYER)
return;
if (Player* target = GetHitPlayer())
if (!handled && target->GetQuestRewardStatus(target->GetTeamId() == TEAM_ALLIANCE ? 11657 : 11923))
{
handled = true;
caster->CastSpell(target, SPELL_GIVE_TORCH, true);
}
caster->CastSpell(caster, SPELL_TORCH_SHADOW_SELF, true);
}
}
void Register() override
{
if (m_scriptSpellId == SPELL_TORCH_CHECK)
OnEffectHitTarget += SpellEffectFn(spell_midsummer_juggling_torch::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
else
AfterCast += SpellCastFn(spell_midsummer_juggling_torch::HandleFinish);
AfterCast += SpellCastFn(spell_midsummer_juggling_torch::HandleFinish);
}
};
@@ -510,6 +547,7 @@ void AddSC_event_midsummer_scripts()
// Spells
RegisterSpellScript(spell_gen_crab_disguise);
RegisterSpellScript(spell_midsummer_ribbon_pole);
RegisterSpellScript(spell_midsummer_ribbon_pole_visual);
RegisterSpellScript(spell_midsummer_torch_quest);
RegisterSpellScript(spell_midsummer_fling_torch);
RegisterSpellScript(spell_midsummer_juggling_torch);

View File

@@ -366,6 +366,9 @@ public:
return;
}
if (m_pInstance)
m_pInstance->SetData(TYPE_ALGALON, FAIL);
ScriptedAI::EnterEvadeMode(why);
}

View File

@@ -786,6 +786,18 @@ public:
go->SetGoState(data == IN_PROGRESS ? GO_STATE_ACTIVE : GO_STATE_READY);
go->EnableCollision(false);
}
if (data == FAIL)
{
scheduler.Schedule(5s, [this](TaskContext)
{
if (m_algalonTimer && (m_algalonTimer <= 60 || m_algalonTimer == TIMER_ALGALON_TO_SUMMON))
{
instance->SummonCreature(NPC_ALGALON, AlgalonLandPos);
}
});
}
break;
// Achievement
@@ -1109,6 +1121,8 @@ public:
void Update(uint32 diff) override
{
InstanceScript::Update(diff);
if (_events.Empty())
return;

View File

@@ -83,13 +83,13 @@ struct boss_anzu : public BossAI
uint32 talkTimer;
void SummonedCreatureDies(Creature* summon, Unit*) override
void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
{
if (summon->GetEntry() == NPC_BROOD_OF_ANZU)
{
summons.Despawn(summon);
summons.RemoveNotExisting();
if (summons.empty())
if (!summons.HasEntry(NPC_BROOD_OF_ANZU))
{
me->RemoveAurasDueToSpell(SPELL_BANISH_SELF);
}

View File

@@ -18,6 +18,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "serpent_shrine.h"
#include "TaskScheduler.h"
enum Talk
{
@@ -32,26 +33,41 @@ enum Talk
enum Spells
{
//Fathomlord Karathress
SPELL_CATACLYSMIC_BOLT = 38441,
SPELL_SEAR_NOVA = 38445,
SPELL_ENRAGE = 24318,
SPELL_BLESSING_OF_THE_TIDES = 38449
SPELL_BLESSING_OF_THE_TIDES = 38449,
//Fathomguard Sharkkis
SPELL_HURL_TRIDENT = 38374,
SPELL_LEECHING_THROW = 29436,
SPELL_MULTI_TOSS = 38366,
SPELL_SUMMON_FATHOM_SPOREBAT = 38431,
SPELL_SUMMON_FATHOM_LURKER = 38433,
SPELL_THE_BEAST_WITHIN = 38373,
SPELL_BESTIAL_WRATH = 38371,
SPELL_POWER_OF_SHARKKIS = 38455,
//Fathomguard Tidalvess
SPELL_FROST_SHOCK = 38234,
SPELL_EARTHBIND_TOTEM = 38304,
SPELL_POISON_CLEANSING_TOTEM = 38306,
SPELL_SPITFIRE_TOTEM = 38236,
SPELL_POWER_OF_TIDALVESS = 38452,
//Fathomguard Caribdis
SPELL_SUMMON_CYCLONE = 38337,
SPELL_WATER_BOLT_VOLLEY = 38335,
SPELL_TIDAL_SURGE = 38358,
SPELL_HEALING_WAVE = 38330,
SPELL_POWER_OF_CARIBDIS = 38451,
//Spitfire Totem
SPELL_ATTACK = 38296
};
enum Misc
{
MAX_ADVISORS = 3,
NPC_FATHOM_GUARD_CARIBDIS = 21964,
NPC_FATHOM_GUARD_TIDALVESS = 21965,
NPC_FATHOM_GUARD_SHARKKIS = 21966,
NPC_SEER_OLUM = 22820,
GO_CAGE = 185952,
EVENT_SPELL_CATACLYSMIC_BOLT = 1,
EVENT_SPELL_ENRAGE = 2,
EVENT_SPELL_SEAR_NOVA = 3,
EVENT_HEALTH_CHECK = 4,
EVENT_KILL_TALK = 5
};
const Position advisorsPosition[MAX_ADVISORS + 2] =
@@ -63,124 +79,463 @@ const Position advisorsPosition[MAX_ADVISORS + 2] =
{457.37f, -544.71f, -7.54f, 0.00f}
};
class boss_fathomlord_karathress : public CreatureScript
struct boss_fathomlord_karathress : public BossAI
{
public:
boss_fathomlord_karathress() : CreatureScript("boss_fathomlord_karathress") { }
CreatureAI* GetAI(Creature* creature) const override
boss_fathomlord_karathress(Creature* creature) : BossAI(creature, DATA_FATHOM_LORD_KARATHRESS)
{
return GetSerpentShrineAI<boss_fathomlord_karathressAI>(creature);
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_fathomlord_karathressAI : public BossAI
void Reset() override
{
boss_fathomlord_karathressAI(Creature* creature) : BossAI(creature, DATA_FATHOM_LORD_KARATHRESS)
{
}
BossAI::Reset();
_recentlySpoken = false;
void Reset() override
{
BossAI::Reset();
me->SummonCreature(NPC_FATHOM_GUARD_TIDALVESS, advisorsPosition[0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 600000);
me->SummonCreature(NPC_FATHOM_GUARD_SHARKKIS, advisorsPosition[1], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 600000);
me->SummonCreature(NPC_FATHOM_GUARD_CARIBDIS, advisorsPosition[2], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 600000);
me->SummonCreature(NPC_FATHOM_GUARD_TIDALVESS, advisorsPosition[0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 600000);
me->SummonCreature(NPC_FATHOM_GUARD_SHARKKIS, advisorsPosition[1], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 600000);
me->SummonCreature(NPC_FATHOM_GUARD_CARIBDIS, advisorsPosition[2], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 600000);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (summon->GetEntry() == NPC_SEER_OLUM)
ScheduleHealthCheckEvent(75, [&]{
for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr)
{
summon->SetWalk(true);
summon->GetMotionMaster()->MovePoint(0, advisorsPosition[MAX_ADVISORS + 1], false);
}
}
void SummonedCreatureDies(Creature* summon, Unit*) override
{
summons.Despawn(summon);
if (summon->GetEntry() == NPC_FATHOM_GUARD_TIDALVESS)
Talk(SAY_GAIN_ABILITY1);
if (summon->GetEntry() == NPC_FATHOM_GUARD_SHARKKIS)
Talk(SAY_GAIN_ABILITY2);
if (summon->GetEntry() == NPC_FATHOM_GUARD_CARIBDIS)
Talk(SAY_GAIN_ABILITY3);
}
void KilledUnit(Unit* /*victim*/) override
{
if (events.GetNextEventTime(EVENT_KILL_TALK) == 0)
{
Talk(SAY_SLAY);
events.ScheduleEvent(EVENT_KILL_TALK, 6000);
}
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
me->SummonCreature(NPC_SEER_OLUM, advisorsPosition[MAX_ADVISORS], TEMPSUMMON_TIMED_DESPAWN, 3600000);
if (GameObject* gobject = me->FindNearestGameObject(GO_CAGE, 100.0f))
gobject->SetGoState(GO_STATE_ACTIVE);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
me->CallForHelp(10.0f);
events.ScheduleEvent(EVENT_SPELL_CATACLYSMIC_BOLT, 10000);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 600000);
events.ScheduleEvent(EVENT_SPELL_SEAR_NOVA, 25000);
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1000);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_ENRAGE:
me->CastSpell(me, SPELL_ENRAGE, true);
break;
case EVENT_SPELL_CATACLYSMIC_BOLT:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, PowerUsersSelector(me, POWER_MANA, 50.0f, true)))
me->CastSpell(target, SPELL_CATACLYSMIC_BOLT, false);
events.ScheduleEvent(EVENT_SPELL_CATACLYSMIC_BOLT, 6000);
break;
case EVENT_SPELL_SEAR_NOVA:
me->CastSpell(me, SPELL_SEAR_NOVA, false);
events.ScheduleEvent(EVENT_SPELL_SEAR_NOVA, 20000 + urand(0, 20000));
break;
case EVENT_HEALTH_CHECK:
if (me->HealthBelowPct(76))
if (Creature* summon = ObjectAccessor::GetCreature(*me, *itr))
{
if (summon->GetMaxHealth() > 500000)
{
for (SummonList::const_iterator itr = summons.begin(); itr != summons.end(); ++itr)
if (Creature* summon = ObjectAccessor::GetCreature(*me, *itr))
if (summon->GetMaxHealth() > 500000)
summon->CastSpell(me, SPELL_BLESSING_OF_THE_TIDES, true);
if (me->HasAura(SPELL_BLESSING_OF_THE_TIDES))
Talk(SAY_GAIN_BLESSING);
break;
summon->CastSpell(me, SPELL_BLESSING_OF_THE_TIDES, true);
}
events.ScheduleEvent(EVENT_HEALTH_CHECK, 1000);
break;
}
}
if (me->HasAura(SPELL_BLESSING_OF_THE_TIDES))
{
Talk(SAY_GAIN_BLESSING);
}
});
}
DoMeleeAttackIfReady();
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (summon->GetEntry() == NPC_SEER_OLUM)
{
summon->SetWalk(true);
summon->GetMotionMaster()->MovePoint(0, advisorsPosition[MAX_ADVISORS + 1], false);
}
};
}
void SummonedCreatureDies(Creature* summon, Unit*) override
{
summons.Despawn(summon);
if (summon->GetEntry() == NPC_FATHOM_GUARD_TIDALVESS)
Talk(SAY_GAIN_ABILITY1);
if (summon->GetEntry() == NPC_FATHOM_GUARD_SHARKKIS)
Talk(SAY_GAIN_ABILITY2);
if (summon->GetEntry() == NPC_FATHOM_GUARD_CARIBDIS)
Talk(SAY_GAIN_ABILITY3);
}
void KilledUnit(Unit* /*victim*/) override
{
if (!_recentlySpoken)
{
Talk(SAY_SLAY);
_recentlySpoken = true;
}
scheduler.Schedule(6s, [this](TaskContext)
{
_recentlySpoken = false;
});
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
me->SummonCreature(NPC_SEER_OLUM, advisorsPosition[MAX_ADVISORS], TEMPSUMMON_TIMED_DESPAWN, 3600000);
if (GameObject* gobject = me->FindNearestGameObject(GO_CAGE, 100.0f))
{
gobject->SetGoState(GO_STATE_ACTIVE);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
instance->DoForAllMinions(DATA_FATHOM_LORD_KARATHRESS, [&](Creature* fathomguard) {
fathomguard->SetInCombatWithZone();
});
scheduler.Schedule(10s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, PowerUsersSelector(me, POWER_MANA, 50.0f, true)))
{
me->CastSpell(target, SPELL_CATACLYSMIC_BOLT);
}
context.Repeat(6s);
}).Schedule(25s, [this](TaskContext context)
{
DoCastSelf(SPELL_SEAR_NOVA);
context.Repeat(20s, 40s);
}).Schedule(10min, [this](TaskContext)
{
DoCastSelf(SPELL_ENRAGE, true);
});
}
private:
bool _recentlySpoken;
};
struct LeechingThrowSelector
{
public:
explicit LeechingThrowSelector(WorldObject const* source) : _source(source) { }
bool operator() (Unit* unit) const
{
return unit->getPowerType() == POWER_MANA && _source->GetDistance(unit) < 50.0f;
}
private:
WorldObject const* _source;
};
struct boss_fathomguard_sharkkis : public ScriptedAI
{
boss_fathomguard_sharkkis(Creature* creature) : ScriptedAI(creature), summons(creature)
{
summons.clear();
_instance = creature->GetInstanceScript();
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
SummonList summons;
void Reset() override
{
_scheduler.CancelAll();
summons.DespawnAll();
}
void JustSummoned(Creature* summon) override
{
summon->SetInCombatWithZone();
summons.Summon(summon);
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(2500ms, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_HURL_TRIDENT);
context.Repeat(5s);
}).Schedule(20650ms, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_MULTI_TOSS);
context.Repeat(12150ms, 26350ms);
}).Schedule(6050ms, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, LeechingThrowSelector(me)))
{
me->CastSpell(target, SPELL_LEECHING_THROW);
}
context.Repeat(6050ms, 22250ms);
}).Schedule(41250ms, [this](TaskContext context)
{
DoCastSelf(SPELL_THE_BEAST_WITHIN);
summons.DoForAllSummons([&](WorldObject* summon)
{
me->CastSpell(summon->ToCreature(), SPELL_BESTIAL_WRATH, true);
});
context.Repeat(39950ms, 46050ms);
}).Schedule(14550ms, [this](TaskContext context)
{
DoCastSelf(urand(0, 1) ? SPELL_SUMMON_FATHOM_LURKER : SPELL_SUMMON_FATHOM_SPOREBAT);
context.Repeat(30300ms);
});
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* karathress = _instance->GetCreature(DATA_FATHOM_LORD_KARATHRESS))
{
me->CastSpell(karathress, SPELL_POWER_OF_SHARKKIS);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
{
return;
}
_scheduler.Update(diff);
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
InstanceScript* _instance;
};
enum NPCTotems
{
NPC_SPITFIRE_TOTEM = 22091,
NPC_GREATER_EARTHBIND_TOTEM = 22486,
NPC_GREATER_POISON_CLEANSING_TOTEM = 22487
};
enum TidalActions
{
ACTION_REMOVE_SPITFIRE = 1,
ACTION_REMOVE_EARTHBIND = 2,
ACTION_REMOVE_CLEANSING = 3
};
enum TotemChoice
{
SPITFIRE = 1,
EARTHBIND = 2,
CLEANSING = 3
};
struct boss_fathomguard_tidalvess : public ScriptedAI
{
boss_fathomguard_tidalvess(Creature* creature) : ScriptedAI(creature), summons(creature)
{
_instance = creature->GetInstanceScript();
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
SummonList summons;
std::list<uint32> entryList;
void Reset() override
{
_scheduler.CancelAll();
_choice = 0;
summons.DespawnAll();
entryList.clear();
entryList = {NPC_SPITFIRE_TOTEM, NPC_GREATER_EARTHBIND_TOTEM, NPC_GREATER_POISON_CLEANSING_TOTEM};
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->Attack(me->GetVictim(), false);
summon->SetInCombatWithZone();
}
void ScheduleRemoval(uint32 entry)
{
std::chrono::seconds timer = 0s;
int32 action = 0;
uint8 group = 0;
switch(entry)
{
case NPC_SPITFIRE_TOTEM:
timer = 59s;
action = ACTION_REMOVE_SPITFIRE;
group = SPITFIRE;
break;
case NPC_GREATER_EARTHBIND_TOTEM:
timer = 44s;
action = ACTION_REMOVE_EARTHBIND;
group = EARTHBIND;
break;
case NPC_GREATER_POISON_CLEANSING_TOTEM:
timer = 29s;
action = ACTION_REMOVE_CLEANSING;
group = CLEANSING;
break;
default:
timer = 29s;
}
_totemScheduler.Schedule(timer, group, [this, action](TaskContext)
{
me->AI()->DoAction(action);
});
}
void DoAction(int32 action) override
{
switch (action)
{
case ACTION_REMOVE_SPITFIRE:
_totemScheduler.CancelGroup(SPITFIRE);
entryList.push_back(NPC_SPITFIRE_TOTEM);
break;
case ACTION_REMOVE_EARTHBIND:
_totemScheduler.CancelGroup(EARTHBIND);
entryList.push_back(NPC_GREATER_EARTHBIND_TOTEM);
break;
case ACTION_REMOVE_CLEANSING:
_totemScheduler.CancelGroup(CLEANSING);
entryList.push_back(NPC_GREATER_POISON_CLEANSING_TOTEM);
break;
default:
return;
}
}
void SummonTotem(uint32 entry)
{
switch(entry)
{
case NPC_SPITFIRE_TOTEM:
DoCastSelf(SPELL_SPITFIRE_TOTEM);
break;
case NPC_GREATER_EARTHBIND_TOTEM:
DoCastSelf(SPELL_EARTHBIND_TOTEM);
break;
case NPC_GREATER_POISON_CLEANSING_TOTEM:
DoCastSelf(SPELL_POISON_CLEANSING_TOTEM);
break;
default:
return;
}
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(10900ms, [this](TaskContext context)
{
DoCastVictim(SPELL_FROST_SHOCK);
context.Repeat(10900ms, 14700ms);
}).Schedule(15800ms, [this](TaskContext context)
{
if (entryList.size() != 0) //don't summon when all totems are up
{
uint32 totemEntry = entryList.front();
entryList.pop_front();
SummonTotem(totemEntry);
ScheduleRemoval(totemEntry);
}
context.Repeat(13350ms, 24250ms);
});
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* karathress = _instance->GetCreature(DATA_FATHOM_LORD_KARATHRESS))
{
me->CastSpell(karathress, SPELL_POWER_OF_TIDALVESS);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
{
return;
}
_scheduler.Update(diff);
_totemScheduler.Update(diff);
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
TaskScheduler _totemScheduler;
InstanceScript* _instance;
uint8 _choice;
};
struct boss_fathomguard_caribdis : public ScriptedAI
{
boss_fathomguard_caribdis(Creature* creature) : ScriptedAI(creature), summons(creature)
{
_instance = creature->GetInstanceScript();
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
SummonList summons;
void Reset() override
{
_scheduler.CancelAll();
summons.DespawnAll();
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(27900ms, [this](TaskContext context)
{
DoCastSelf(SPELL_WATER_BOLT_VOLLEY);
context.Repeat(6050ms, 19750ms);
}).Schedule(23050ms, [this](TaskContext context)
{
DoCastSelf(SPELL_TIDAL_SURGE);
context.Repeat(24250ms, 33250ms);
}).Schedule(15750ms, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_SUMMON_CYCLONE);
context.Repeat(47250ms, 51550ms);
}).Schedule(20s, [this](TaskContext context)
{
if (Unit* target = DoSelectLowestHpFriendly(60.0f, 150000))
{
DoCast(target, SPELL_HEALING_WAVE);
}
context.Repeat(20s);
});
}
void JustDied(Unit* /*killer*/) override
{
if (Creature* karathress = _instance->GetCreature(DATA_FATHOM_LORD_KARATHRESS))
{
me->CastSpell(karathress, SPELL_POWER_OF_CARIBDIS);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
{
return;
}
_scheduler.Update(diff);
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
InstanceScript* _instance;
};
class spell_karathress_power_of_caribdis : public SpellScriptLoader
@@ -213,6 +568,9 @@ public:
void AddSC_boss_fathomlord_karathress()
{
new boss_fathomlord_karathress();
RegisterSerpentShrineAI(boss_fathomlord_karathress);
RegisterSerpentShrineAI(boss_fathomguard_sharkkis);
RegisterSerpentShrineAI(boss_fathomguard_tidalvess);
RegisterSerpentShrineAI(boss_fathomguard_caribdis);
new spell_karathress_power_of_caribdis();
}

View File

@@ -64,20 +64,6 @@ enum Misc
ITEM_TAINTED_CORE = 31088,
POINT_HOME = 1,
EVENT_SPELL_SHOCK_BLAST = 1,
EVENT_SPELL_STATIC_CHARGE = 2,
EVENT_SPELL_ENTANGLE = 3,
EVENT_CHECK_HEALTH = 4,
EVENT_SPELL_FORKED_LIGHTNING = 5,
EVENT_SUMMON_A = 6,
EVENT_SUMMON_B = 7,
EVENT_SUMMON_C = 8,
EVENT_SUMMON_D = 9,
EVENT_CHECK_HEALTH2 = 10,
EVENT_SUMMON_SPOREBAT = 11,
EVENT_KILL_TALK = 20
};
class startFollow : public BasicEvent
@@ -88,215 +74,223 @@ public:
bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override
{
if (InstanceScript* instance = _owner->GetInstanceScript())
{
if (Creature* vashj = ObjectAccessor::GetCreature(*_owner, instance->GetGuidData(NPC_LADY_VASHJ)))
{
_owner->GetMotionMaster()->MoveFollow(vashj, 3.0f, vashj->GetAngle(_owner), MOTION_SLOT_CONTROLLED);
}
}
return true;
}
private:
Unit* _owner;
};
class boss_lady_vashj : public CreatureScript
struct boss_lady_vashj : public BossAI
{
public:
boss_lady_vashj() : CreatureScript("boss_lady_vashj") { }
CreatureAI* GetAI(Creature* creature) const override
boss_lady_vashj(Creature* creature) : BossAI(creature, DATA_LADY_VASHJ)
{
return GetSerpentShrineAI<boss_lady_vashjAI>(creature);
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
_intro = false;
}
struct boss_lady_vashjAI : public BossAI
void Reset() override
{
boss_lady_vashjAI(Creature* creature) : BossAI(creature, DATA_LADY_VASHJ)
_count = 0;
_recentlySpoken = false;
_batTimer = 20s;
BossAI::Reset();
ScheduleHealthCheckEvent(70, [&]{
Talk(SAY_PHASE2);
me->SetReactState(REACT_PASSIVE);
me->GetMotionMaster()->MovePoint(POINT_HOME, me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY(), me->GetHomePosition().GetPositionZ(), true, true);
});
}
void KilledUnit(Unit* /*victim*/) override
{
if(!_recentlySpoken)
{
intro = false;
Talk(SAY_SLAY);
_recentlySpoken = true;
}
scheduler.Schedule(6s, [this](TaskContext)
{
_recentlySpoken = false;
});
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
DoCastSelf(SPELL_REMOVE_TAINTED_CORES, true);
ScheduleSpells();
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (summon->GetEntry() == WORLD_TRIGGER)
{
summon->CastSpell(summon, SPELL_MAGIC_BARRIER);
}
else if (summon->GetEntry() == NPC_ENCHANTED_ELEMENTAL)
{
summon->SetWalk(true);
summon->m_Events.AddEvent(new startFollow(summon), summon->m_Events.CalculateTime(0));
}
else if (summon->GetEntry() == NPC_TOXIC_SPOREBAT)
{
summon->GetMotionMaster()->MoveRandom(30.0f);
}
else if (summon->GetEntry() != NPC_TAINTED_ELEMENTAL)
{
summon->GetMotionMaster()->MovePoint(POINT_HOME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), true, true);
}
}
void ScheduleSpells()
{
scheduler.Schedule(14550ms, [this](TaskContext context)
{
DoCastVictim(SPELL_SHOCK_BLAST);
context.Repeat(10850ms, 25100ms);
}).Schedule(18150ms, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_STATIC_CHARGE);
context.Repeat(7250ms, 27050ms);
}).Schedule(25450ms, [this](TaskContext context)
{
DoCastSelf(SPELL_ENTANGLE);
context.Repeat(18200ms, 51500ms);
});
}
void MoveInLineOfSight(Unit* who) override
{
if (!_intro && who->GetTypeId() == TYPEID_PLAYER)
{
_intro = true;
Talk(SAY_INTRO);
}
bool intro;
int32 count;
BossAI::MoveInLineOfSight(who);
}
void Reset() override
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE || id != POINT_HOME)
{
count = 0;
BossAI::Reset();
return;
}
void KilledUnit(Unit* /*victim*/) override
me->SetFacingTo(me->GetHomePosition().GetOrientation());
instance->SetData(DATA_ACTIVATE_SHIELD, 0);
scheduler.CancelAll();
scheduler.Schedule(2400ms, [this](TaskContext context)
{
if (events.GetNextEventTime(EVENT_KILL_TALK) == 0)
DoCastRandomTarget(SPELL_FORKED_LIGHTNING);
context.Repeat(2400ms, 12450ms);
}).Schedule(0s, [this](TaskContext context)
{
DoCastSelf(SPELL_SUMMON_ENCHANTED_ELEMENTAL, true);
context.Repeat(2500ms);
}).Schedule(45s, [this](TaskContext context)
{
DoCastSelf(SPELL_SUMMON_COILFANG_ELITE, true);
context.Repeat(45s);
}).Schedule(60s, [this](TaskContext context)
{
DoCastSelf(SPELL_SUMMON_COILFANG_STRIDER, true);
context.Repeat(60s);
}).Schedule(50s, [this](TaskContext context)
{
DoCastSelf(SPELL_SUMMON_TAINTED_ELEMENTAL, true);
context.Repeat(50s);
}).Schedule(1s, [this](TaskContext context)
{
if (!me->HasAura(SPELL_MAGIC_BARRIER))
{
Talk(SAY_SLAY);
events.ScheduleEvent(EVENT_KILL_TALK, 6000);
}
}
Talk(SAY_PHASE3);
me->SetReactState(REACT_AGGRESSIVE);
me->GetMotionMaster()->MoveChase(me->GetVictim());
scheduler.CancelAll();
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
me->CastSpell(me, SPELL_REMOVE_TAINTED_CORES, true);
events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, 10000);
events.ScheduleEvent(EVENT_SPELL_STATIC_CHARGE, 15000);
events.ScheduleEvent(EVENT_SPELL_ENTANGLE, 20000);
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
if (summon->GetEntry() == WORLD_TRIGGER)
summon->CastSpell(summon, SPELL_MAGIC_BARRIER, false);
else if (summon->GetEntry() == NPC_ENCHANTED_ELEMENTAL)
{
summon->SetWalk(true);
summon->m_Events.AddEvent(new startFollow(summon), summon->m_Events.CalculateTime(0));
}
else if (summon->GetEntry() == NPC_TOXIC_SPOREBAT)
summon->GetMotionMaster()->MoveRandom(30.0f);
else if (summon->GetEntry() != NPC_TAINTED_ELEMENTAL)
summon->GetMotionMaster()->MovePoint(POINT_HOME, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), true, true);
}
void MoveInLineOfSight(Unit* who) override
{
if (!intro && who->GetTypeId() == TYPEID_PLAYER)
{
intro = true;
Talk(SAY_INTRO);
}
BossAI::MoveInLineOfSight(who);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE || id != POINT_HOME)
return;
me->SetFacingTo(me->GetHomePosition().GetOrientation());
instance->SetData(DATA_ACTIVATE_SHIELD, 0);
events.Reset();
events.ScheduleEvent(EVENT_SPELL_FORKED_LIGHTNING, 3000);
events.ScheduleEvent(EVENT_SUMMON_A, 0);
events.ScheduleEvent(EVENT_SUMMON_B, 45000);
events.ScheduleEvent(EVENT_SUMMON_C, 60000);
events.ScheduleEvent(EVENT_SUMMON_D, 50000);
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_SHOCK_BLAST:
me->CastSpell(me->GetVictim(), SPELL_SHOCK_BLAST, false);
events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, urand(10000, 20000));
break;
case EVENT_SPELL_STATIC_CHARGE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f))
me->CastSpell(target, SPELL_STATIC_CHARGE, false);
events.ScheduleEvent(EVENT_SPELL_STATIC_CHARGE, 20000);
break;
case EVENT_SPELL_ENTANGLE:
me->CastSpell(me, SPELL_ENTANGLE, false);
events.ScheduleEvent(EVENT_SPELL_ENTANGLE, 30000);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(71))
{
Talk(SAY_PHASE2);
me->SetReactState(REACT_PASSIVE);
me->GetMotionMaster()->MovePoint(POINT_HOME, me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY(), me->GetHomePosition().GetPositionZ(), true, true);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
break;
case EVENT_SPELL_FORKED_LIGHTNING:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f))
me->CastSpell(target, SPELL_FORKED_LIGHTNING, false);
events.ScheduleEvent(EVENT_SPELL_FORKED_LIGHTNING, urand(2500, 5000));
break;
case EVENT_SUMMON_A:
me->CastSpell(me, SPELL_SUMMON_ENCHANTED_ELEMENTAL, true);
events.ScheduleEvent(EVENT_SUMMON_A, 2500);
break;
case EVENT_SUMMON_B:
me->CastSpell(me, SPELL_SUMMON_COILFANG_ELITE, true);
events.ScheduleEvent(EVENT_SUMMON_B, 45000);
break;
case EVENT_SUMMON_C:
me->CastSpell(me, SPELL_SUMMON_COILFANG_STRIDER, true);
events.ScheduleEvent(EVENT_SUMMON_C, 60000);
break;
case EVENT_SUMMON_D:
me->CastSpell(me, SPELL_SUMMON_TAINTED_ELEMENTAL, true);
events.ScheduleEvent(EVENT_SUMMON_D, 50000);
break;
case EVENT_CHECK_HEALTH2:
if (!me->HasAura(SPELL_MAGIC_BARRIER))
{
Talk(SAY_PHASE3);
me->SetReactState(REACT_AGGRESSIVE);
me->GetMotionMaster()->MoveChase(me->GetVictim());
events.Reset();
events.ScheduleEvent(EVENT_SPELL_SHOCK_BLAST, 10000);
events.ScheduleEvent(EVENT_SPELL_STATIC_CHARGE, 15000);
events.ScheduleEvent(EVENT_SPELL_ENTANGLE, 20000);
events.ScheduleEvent(EVENT_SUMMON_SPOREBAT, 5000);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH2, 1000);
break;
case EVENT_SUMMON_SPOREBAT:
me->CastSpell(me, SPELL_SUMMON_TOXIC_SPOREBAT, true);
events.ScheduleEvent(EVENT_SUMMON_SPOREBAT, 20000 - 1000 * std::min(count++, 16));
break;
}
if (me->GetReactState() != REACT_AGGRESSIVE || !me->isAttackReady())
return;
if (!me->IsWithinMeleeRange(me->GetVictim()))
{
me->resetAttackTimer();
me->SetSheath(SHEATH_STATE_RANGED);
me->CastSpell(me->GetVictim(), roll_chance_i(33) ? SPELL_MULTI_SHOT : SPELL_SHOOT, false);
if (roll_chance_i(15))
Talk(SAY_BOWSHOT);
ScheduleSpells();
scheduler.Schedule(5s, [this](TaskContext context)
{
DoCastSelf(SPELL_SUMMON_TOXIC_SPOREBAT, true);
_batTimer = 20s - static_cast<std::chrono::seconds>(std::min(_count++, 16));
context.Repeat(_batTimer);
});
}
else
{
me->SetSheath(SHEATH_STATE_MELEE);
DoMeleeAttackIfReady();
context.Repeat(1s);
}
});
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->GetReactState() != REACT_AGGRESSIVE || !me->isAttackReady())
{
return;
}
bool CheckEvadeIfOutOfCombatArea() const override
if (!me->IsWithinMeleeRange(me->GetVictim()))
{
return me->GetHomePosition().GetExactDist2d(me) > 80.0f || !SelectTargetFromPlayerList(100.0f);
me->resetAttackTimer();
me->SetSheath(SHEATH_STATE_RANGED);
me->CastSpell(me->GetVictim(), roll_chance_i(33) ? SPELL_MULTI_SHOT : SPELL_SHOOT, false);
if (roll_chance_i(15))
{
Talk(SAY_BOWSHOT);
}
}
};
else
{
me->SetSheath(SHEATH_STATE_MELEE);
DoMeleeAttackIfReady();
}
}
bool CheckEvadeIfOutOfCombatArea() const override
{
return me->GetHomePosition().GetExactDist2d(me) > 80.0f || !SelectTargetFromPlayerList(100.0f);
}
private:
bool _recentlySpoken;
bool _intro;
int32 _count;
std::chrono::seconds _batTimer;
};
/*
//Toxic Sporebat
//Toxic Spores: Used in Phase 3 by the Spore Bats, it creates a contaminated green patch of ground, dealing about 2775-3225 nature damage every second to anyone who stands in it.
//deprecated -- adds do work
class npc_toxic_sporebat : public CreatureScript
{
public:
@@ -433,7 +427,9 @@ public:
{
PreventHitDefaultEffect(effIndex);
if (Player* target = GetHitPlayer())
{
target->DestroyItemCount(ITEM_TAINTED_CORE, -1, true);
}
}
void Register() override
@@ -488,7 +484,9 @@ public:
{
PreventHitDefaultEffect(effIndex);
if (Unit* target = GetHitUnit())
{
target->CastSpell(target, SPELL_TOXIC_SPORES, true, nullptr, nullptr, GetCaster()->GetGUID());
}
}
void Register() override
@@ -505,7 +503,7 @@ public:
void AddSC_boss_lady_vashj()
{
new boss_lady_vashj();
RegisterSerpentShrineAI(boss_lady_vashj);
new spell_lady_vashj_magic_barrier();
new spell_lady_vashj_remove_tainted_cores();
new spell_lady_vashj_summon_sporebat();

View File

@@ -29,6 +29,20 @@ DoorData const doorData[] =
{ GO_COILFANG_BRIDGE3, DATA_BRIDGE_EMERGED, DOOR_TYPE_PASSAGE }
};
ObjectData const creatureData[] =
{
{ NPC_FATHOM_LORD_KARATHRESS, DATA_FATHOM_LORD_KARATHRESS },
{ 0, 0 }
};
MinionData const minionData[] =
{
{ NPC_FATHOM_GUARD_SHARKKIS, DATA_FATHOM_LORD_KARATHRESS },
{ NPC_FATHOM_GUARD_TIDALVESS, DATA_FATHOM_LORD_KARATHRESS },
{ NPC_FATHOM_GUARD_CARIBDIS, DATA_FATHOM_LORD_KARATHRESS },
{ 0, 0, }
};
class instance_serpent_shrine : public InstanceMapScript
{
public:
@@ -45,6 +59,8 @@ public:
SetHeaders(DataHeader);
SetBossNumber(MAX_ENCOUNTERS);
LoadDoorData(doorData);
LoadObjectData(creatureData, nullptr);
LoadMinionData(minionData);
AliveKeepersCount = 0;
}

View File

@@ -53,8 +53,13 @@ enum SSNPCs
NPC_THE_LURKER_BELOW = 21217,
NPC_LEOTHERAS_THE_BLIND = 21215,
NPC_CYCLONE_KARATHRESS = 22104,
NPC_FATHOM_LORD_KARATHRESS = 21214,
NPC_LADY_VASHJ = 21212,
NPC_FATHOM_GUARD_SHARKKIS = 21966,
NPC_FATHOM_GUARD_TIDALVESS = 21965,
NPC_FATHOM_GUARD_CARIBDIS = 21964,
NPC_COILFANG_SHATTERER = 21301,
NPC_COILFANG_PRIESTESS = 21220,

View File

@@ -23,8 +23,7 @@ enum Spells
{
SPELL_ACID_SPRAY = 38153,
SPELL_CLEAVE = 40504,
SPELL_POISON_BOLT_VOLLEY_N = 34780,
SPELL_POISON_BOLT_VOLLEY_H = 39340,
SPELL_POISON_BOLT_VOLLEY = 34780,
SPELL_UPPERCUT = 32055
};
@@ -38,11 +37,6 @@ struct boss_quagmirran : public BossAI
});
}
void Reset() override
{
_Reset();
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
@@ -57,11 +51,11 @@ struct boss_quagmirran : public BossAI
context.Repeat(21800ms);
}).Schedule(25200ms, [this](TaskContext context)
{
DoCastVictim(SPELL_ACID_SPRAY);
DoCastRandomTarget(SPELL_ACID_SPRAY);
context.Repeat(25s);
}).Schedule(31800ms, [this](TaskContext context)
{
DoCastSelf(DUNGEON_MODE(SPELL_POISON_BOLT_VOLLEY_N, SPELL_POISON_BOLT_VOLLEY_H));
DoCastAOE(SPELL_POISON_BOLT_VOLLEY);
context.Repeat(24400ms);
});
}
@@ -70,4 +64,4 @@ struct boss_quagmirran : public BossAI
void AddSC_boss_quagmirran()
{
RegisterTheSlavePensCreatureAI(boss_quagmirran);
}
}

View File

@@ -26,6 +26,12 @@ DoorData const doorData[] =
{ 0, 0, DOOR_TYPE_ROOM } // END
};
ObjectData const creatureData[] =
{
{ NPC_MAULGAR, DATA_MAULGAR },
{ 0, 0 }
};
MinionData const minionData[] =
{
{ NPC_MAULGAR, DATA_MAULGAR },
@@ -46,64 +52,13 @@ public:
{
SetHeaders(DataHeader);
SetBossNumber(MAX_ENCOUNTER);
LoadObjectData(creatureData, nullptr);
LoadDoorData(doorData);
LoadMinionData(minionData);
_addsKilled = 0;
}
void OnCreatureCreate(Creature* creature) override
{
switch (creature->GetEntry())
{
case NPC_MAULGAR:
_maulgarGUID = creature->GetGUID();
[[fallthrough]];
case NPC_KROSH_FIREHAND:
case NPC_OLM_THE_SUMMONER:
case NPC_KIGGLER_THE_CRAZED:
case NPC_BLINDEYE_THE_SEER:
AddMinion(creature, true);
break;
}
}
void OnCreatureRemove(Creature* creature) override
{
switch (creature->GetEntry())
{
case NPC_MAULGAR:
case NPC_KROSH_FIREHAND:
case NPC_OLM_THE_SUMMONER:
case NPC_KIGGLER_THE_CRAZED:
case NPC_BLINDEYE_THE_SEER:
AddMinion(creature, false);
break;
}
}
void OnGameObjectCreate(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_MAULGAR_DOOR:
case GO_GRUUL_DOOR:
AddDoor(go, true);
break;
}
}
void OnGameObjectRemove(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_MAULGAR_DOOR:
case GO_GRUUL_DOOR:
AddDoor(go, false);
break;
}
}
bool SetBossState(uint32 id, EncounterState state) override
{
if (!InstanceScript::SetBossState(id, state))
@@ -117,8 +72,12 @@ public:
void SetData(uint32 type, uint32 /*id*/) override
{
if (type == DATA_ADDS_KILLED)
if (Creature* maulgar = instance->GetCreature(_maulgarGUID))
{
if (Creature* maulgar = GetCreature(DATA_MAULGAR))
{
maulgar->AI()->DoAction(++_addsKilled);
}
}
}
uint32 GetData(uint32 type) const override
@@ -130,7 +89,6 @@ public:
protected:
uint32 _addsKilled;
ObjectGuid _maulgarGUID;
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override

View File

@@ -388,20 +388,25 @@ class spell_dru_treant_scaling : public AuraScript
};
// -1850 - Dash
class spell_dru_dash : public AuraScript
class spell_dru_dash : public SpellScript
{
PrepareAuraScript(spell_dru_dash);
PrepareSpellScript(spell_dru_dash);
void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/)
SpellCastResult CheckCast()
{
// do not set speed if not in cat form
if (GetUnitOwner()->GetShapeshiftForm() != FORM_CAT)
amount = 0;
Unit* caster = GetCaster();
if (caster->GetShapeshiftForm() != FORM_CAT)
{
SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_BE_IN_CAT_FORM);
return SPELL_FAILED_CUSTOM_ERROR;
}
return SPELL_CAST_OK;
}
void Register() override
{
DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_dash::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_INCREASE_SPEED);
OnCheckCast += SpellCheckCastFn(spell_dru_dash::CheckCast);
}
};

View File

@@ -1308,6 +1308,29 @@ class spell_hun_bestial_wrath : public SpellScript
}
};
class spell_hun_furious_howl : public SpellScript
{
PrepareSpellScript(spell_hun_furious_howl);
bool Load() override
{
return GetCaster()->IsPet();
}
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if([&](WorldObject const* target) -> bool
{
return target != GetCaster() && target != GetCaster()->ToPet()->GetOwner();
});
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hun_furious_howl::FilterTargets, EFFECT_ALL, TARGET_UNIT_CASTER_AREA_PARTY);
}
};
void AddSC_hunter_spell_scripts()
{
RegisterSpellScript(spell_hun_check_pet_los);
@@ -1338,4 +1361,5 @@ void AddSC_hunter_spell_scripts()
RegisterSpellScript(spell_hun_lock_and_load);
RegisterSpellScript(spell_hun_intimidation);
RegisterSpellScript(spell_hun_bestial_wrath);
RegisterSpellScript(spell_hun_furious_howl);
}

View File

@@ -3342,9 +3342,9 @@ class spell_item_rocket_boots : public SpellScript
}
};
class spell_item_runic_healing_injector : public SpellScript
class spell_item_healing_injector : public SpellScript
{
PrepareSpellScript(spell_item_runic_healing_injector);
PrepareSpellScript(spell_item_healing_injector);
bool Load() override
{
@@ -3360,7 +3360,33 @@ class spell_item_runic_healing_injector : public SpellScript
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_item_runic_healing_injector::HandleHeal, EFFECT_0, SPELL_EFFECT_HEAL);
OnEffectHitTarget += SpellEffectFn(spell_item_healing_injector::HandleHeal, EFFECT_0, SPELL_EFFECT_HEAL);
}
};
class spell_item_mana_injector : public SpellScript
{
PrepareSpellScript(spell_item_mana_injector);
bool Load() override
{
return GetCaster()->GetTypeId() == TYPEID_PLAYER;
}
void HandleEnergize(SpellEffIndex /*effIndex*/)
{
if (Player* caster = GetCaster()->ToPlayer())
{
if (caster->HasSkill(SKILL_ENGINEERING))
{
SetEffectValue(GetEffectValue() * 1.25f);
}
}
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_item_mana_injector::HandleEnergize, EFFECT_0, SPELL_EFFECT_ENERGIZE);
}
};
@@ -3959,7 +3985,8 @@ void AddSC_item_spell_scripts()
RegisterSpellScript(spell_item_nitro_boots);
RegisterSpellScript(spell_item_teach_language);
RegisterSpellScript(spell_item_rocket_boots);
RegisterSpellScript(spell_item_runic_healing_injector);
RegisterSpellScript(spell_item_healing_injector);
RegisterSpellScript(spell_item_mana_injector);
RegisterSpellScript(spell_item_pygmy_oil);
RegisterSpellScript(spell_item_unusual_compass);
RegisterSpellScript(spell_item_chicken_cover);

View File

@@ -1267,6 +1267,45 @@ class spell_warl_glyph_of_felguard : public AuraScript
}
};
class spell_warl_glyph_of_voidwalker : public AuraScript
{
PrepareAuraScript(spell_warl_glyph_of_voidwalker);
void HandleApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{
if (Player* player = GetCaster()->ToPlayer())
{
if (Pet* pet = player->GetPet())
{
if (pet->GetEntry() == NPC_VOIDWALKER)
{
pet->HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_PCT, aurEff->GetAmount(), true);
}
}
}
}
void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
{
if (Player* player = GetCaster()->ToPlayer())
{
if (Pet* pet = player->GetPet())
{
if (pet->GetEntry() == NPC_VOIDWALKER)
{
pet->HandleStatModifier(UNIT_MOD_STAT_STAMINA, TOTAL_PCT, aurEff->GetAmount(), false);
}
}
}
}
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_warl_glyph_of_voidwalker::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
OnEffectRemove += AuraEffectRemoveFn(spell_warl_glyph_of_voidwalker::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
};
void AddSC_warlock_spell_scripts()
{
RegisterSpellScript(spell_warl_eye_of_kilrogg);
@@ -1299,4 +1338,5 @@ void AddSC_warlock_spell_scripts()
RegisterSpellScript(spell_warl_drain_soul);
RegisterSpellScript(spell_warl_shadowburn);
RegisterSpellScript(spell_warl_glyph_of_felguard);
RegisterSpellScript(spell_warl_glyph_of_voidwalker);
}