Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2023-06-22 09:53:06 +08:00
15 changed files with 488 additions and 360 deletions

View File

@@ -119,6 +119,47 @@ struct boss_malchezaar : public BossAI
{
Initialize();
_Reset();
ScheduleHealthCheckEvent(60, [&] {
me->InterruptNonMeleeSpells(false);
_phase = 2;
DoCastSelf(SPELL_EQUIP_AXES);
Talk(SAY_AXE_TOSS1);
DoCastSelf(SPELL_THRASH_AURA, true);
SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE, EQUIP_NO_CHANGE);
me->SetCanDualWield(true);
me->SetAttackTime(OFF_ATTACK, (me->GetAttackTime(BASE_ATTACK) * 150) / 100);
scheduler.Schedule(5s, 10s, [this](TaskContext context)
{
DoCastVictim(SPELL_SUNDER_ARMOR);
context.Repeat();
});
scheduler.CancelGroup(GROUP_SHADOW_WORD_PAIN);
});
ScheduleHealthCheckEvent(30, [&] {
me->RemoveAurasDueToSpell(SPELL_THRASH_AURA);
Talk(SAY_AXE_TOSS2);
_phase = PHASE_THREE;
clearweapons();
me->SummonCreature(NPC_MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
scheduler.Schedule(20s, 30s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_AMPLIFY_DAMAGE, 1);
context.Repeat();
}).Schedule(20s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN);
context.SetGroup(GROUP_SHADOW_WORD_PAIN);
context.Repeat();
});
scheduler.CancelGroup(GROUP_ENFEEBLE);
});
}
void KilledUnit(Unit* /*victim*/) override
@@ -183,49 +224,6 @@ struct boss_malchezaar : public BossAI
});
}
void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (me->HealthBelowPctDamaged(60, damage) && _phase == PHASE_ONE)
{
me->InterruptNonMeleeSpells(false);
_phase = 2;
DoCastSelf( SPELL_EQUIP_AXES);
Talk(SAY_AXE_TOSS1);
DoCastSelf( SPELL_THRASH_AURA, true);
SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE, EQUIP_NO_CHANGE);
me->SetCanDualWield(true);
me->SetAttackTime(OFF_ATTACK, (me->GetAttackTime(BASE_ATTACK) * 150) / 100);
scheduler.Schedule(5s, 10s, [this](TaskContext context)
{
DoCastVictim(SPELL_SUNDER_ARMOR);
context.Repeat();
});
scheduler.CancelGroup(GROUP_SHADOW_WORD_PAIN);
}
else if (me->HealthBelowPctDamaged(30, damage) && _phase == PHASE_TWO)
{
me->RemoveAurasDueToSpell(SPELL_THRASH_AURA);
Talk(SAY_AXE_TOSS2);
_phase = PHASE_THREE;
clearweapons();
me->SummonCreature(NPC_MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
scheduler.Schedule(20s, 30s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_AMPLIFY_DAMAGE, 1);
context.Repeat();
}).Schedule(20s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN);
context.SetGroup(GROUP_SHADOW_WORD_PAIN);
context.Repeat();
});;
}
}
void EnfeebleHealthEffect()
{
std::list<Unit*> targetList;

View File

@@ -56,7 +56,8 @@ public:
_currentRift = 0;
_shieldPercent = 100;
_encounterNPCs.clear();
_canSpawnPortal = true; // Delay after bosses
_noBossSpawnDelay = true; // Delay after bosses
_eventStatus = EVENT_PREPARE;
}
void CleanupInstance()
@@ -72,10 +73,24 @@ public:
_availableRiftPositions.push_back(pos);
}
// prevent getting stuck if event fails during boss break
_noBossSpawnDelay = true;
instance->LoadGrid(-2023.0f, 7121.0f);
if (Creature* medivh = GetCreature(DATA_MEDIVH))
{
medivh->DespawnOrUnsummon(0ms, 3s);
medivh->Respawn();
}
for (ObjectGuid const& guid : _encounterNPCs)
{
if (guid.GetEntry() == NPC_DP_BEAM_STALKER)
{
if (Creature* creature = instance->GetCreature(guid))
{
creature->Respawn();
}
break;
}
}
}
@@ -121,8 +136,8 @@ public:
case NPC_RIFT_LORD:
case NPC_RIFT_LORD_2:
case NPC_TIME_RIFT:
case NPC_INFINITE_ASSASIN:
case NPC_INFINITE_ASSASIN_2:
case NPC_INFINITE_ASSASSIN:
case NPC_INFINITE_ASSASSIN_2:
case NPC_INFINITE_WHELP:
case NPC_INFINITE_CHRONOMANCER:
case NPC_INFINITE_CHRONOMANCER_2:
@@ -143,14 +158,14 @@ public:
case DATA_CHRONO_LORD_DEJA:
case DATA_TEMPORUS:
{
_canSpawnPortal = false;
_noBossSpawnDelay = false;
_scheduler.Schedule(2min + 30s, [this](TaskContext)
{
_canSpawnPortal = true;
_noBossSpawnDelay = true;
ScheduleNextPortal(0s, Position(0.0f, 0.0f, 0.0f, 0.0f));
});
ScheduleNextPortal(2min + 30s, Position(0.0f, 0.0f, 0.0f, 0.0f));
break;
}
default:
@@ -163,26 +178,27 @@ public:
void OnPlayerEnter(Player* player) override
{
if (instance->GetPlayersCountExceptGMs() <= 1 && GetBossState(DATA_AEONUS) != DONE)
if (instance->GetPlayersCountExceptGMs() <= 1 && GetBossState(DATA_AEONUS) != DONE && _eventStatus != EVENT_IN_PROGRESS)
{
CleanupInstance();
}
player->SendUpdateWorldState(WORLD_STATE_BM, _currentRift > 0 ? 1 : 0);
player->SendUpdateWorldState(WORLD_STATE_BM, _eventStatus);
player->SendUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
player->SendUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
}
void ScheduleNextPortal(Milliseconds time, Position lastPosition)
{
// only one rift can be scheduled at any time
_scheduler.CancelGroup(CONTEXT_GROUP_RIFTS);
_scheduler.Schedule(time, [this, lastPosition](TaskContext context)
{
if (GetCreature(DATA_MEDIVH))
{
// Spawning prevented - there's a 150s delay after a boss dies.
if (!_canSpawnPortal)
// Spawning prevented: after-boss-delay or event failed/not started or last portal spawned
if (!_noBossSpawnDelay || _eventStatus == EVENT_PREPARE || _currentRift >= 18)
{
return;
}
@@ -208,19 +224,10 @@ public:
instance->SummonCreature(NPC_TIME_RIFT, spawnPos);
// Here we check if we have available rift spots.
if (_currentRift < 18)
{
if (!_availableRiftPositions.empty())
{
context.Repeat((_currentRift >= 13 ? 2min : 90s));
}
else
{
context.Repeat(4s);
}
}
// queue next portal if group doesn't kill keepers fast enough
context.Repeat((_currentRift >= 13 ? 2min : 90s));
}
// if no rift positions are available, the next rift will be scheduled in OnCreatureRemove
}
context.SetGroup(CONTEXT_GROUP_RIFTS);
@@ -241,8 +248,8 @@ public:
case NPC_RIFT_KEEPER_MAGE:
case NPC_RIFT_LORD:
case NPC_RIFT_LORD_2:
case NPC_INFINITE_ASSASIN:
case NPC_INFINITE_ASSASIN_2:
case NPC_INFINITE_ASSASSIN:
case NPC_INFINITE_ASSASSIN_2:
case NPC_INFINITE_WHELP:
case NPC_INFINITE_CHRONOMANCER:
case NPC_INFINITE_CHRONOMANCER_2:
@@ -251,6 +258,7 @@ public:
case NPC_INFINITE_VANQUISHER:
case NPC_INFINITE_VANQUISHER_2:
case NPC_DP_BEAM_STALKER:
case NPC_DP_EMITTER_STALKER:
_encounterNPCs.insert(creature->GetGUID());
break;
}
@@ -263,7 +271,7 @@ public:
switch (creature->GetEntry())
{
case NPC_TIME_RIFT:
if (_currentRift < 18)
if (_currentRift < 18 && _noBossSpawnDelay && _eventStatus == EVENT_IN_PROGRESS)
{
if (_availableRiftPositions.size() < 3)
{
@@ -286,8 +294,8 @@ public:
case NPC_RIFT_KEEPER_MAGE:
case NPC_RIFT_LORD:
case NPC_RIFT_LORD_2:
case NPC_INFINITE_ASSASIN:
case NPC_INFINITE_ASSASIN_2:
case NPC_INFINITE_ASSASSIN:
case NPC_INFINITE_ASSASSIN_2:
case NPC_INFINITE_WHELP:
case NPC_INFINITE_CHRONOMANCER:
case NPC_INFINITE_CHRONOMANCER_2:
@@ -295,6 +303,7 @@ public:
case NPC_INFINITE_EXECUTIONER_2:
case NPC_INFINITE_VANQUISHER:
case NPC_INFINITE_VANQUISHER_2:
case NPC_DP_EMITTER_STALKER:
_encounterNPCs.erase(creature->GetGUID());
break;
}
@@ -308,27 +317,14 @@ public:
{
case DATA_MEDIVH:
{
DoUpdateWorldState(WORLD_STATE_BM, 1);
_eventStatus = EVENT_IN_PROGRESS;
DoUpdateWorldState(WORLD_STATE_BM, _eventStatus);
DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
ScheduleNextPortal(3s, Position(0.0f, 0.0f, 0.0f, 0.0f));
for (ObjectGuid const& guid : _encounterNPCs)
{
if (guid.GetEntry() == NPC_DP_BEAM_STALKER)
{
if (Creature* creature = instance->GetCreature(guid))
{
if (!creature->IsAlive())
{
creature->Respawn(true);
}
}
break;
}
}
break;
}
case DATA_DAMAGE_SHIELD:
@@ -348,6 +344,8 @@ public:
if (!_shieldPercent)
{
_eventStatus = EVENT_PREPARE;
if (Creature* medivh = GetCreature(DATA_MEDIVH))
{
if (medivh->IsAlive() && medivh->IsAIEnabled)
@@ -404,6 +402,12 @@ public:
GuidSet encounterNPCSCopy = _encounterNPCs;
for (ObjectGuid const& guid : encounterNPCSCopy)
{
// Don't despawn permanent visual effect NPC twice or it won't respawn correctly
if (guid.GetEntry() == NPC_DP_BEAM_STALKER)
{
continue;
}
if (Creature* creature = instance->GetCreature(guid))
{
creature->CastSpell(creature, SPELL_TELEPORT_VISUAL, true);
@@ -412,6 +416,16 @@ public:
}
_scheduler.CancelAll();
// Step 4 - Schedule instance cleanup without player interaction
_scheduler.Schedule(300s, [this](TaskContext)
{
CleanupInstance();
DoUpdateWorldState(WORLD_STATE_BM, _eventStatus);
DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
});
});
});
});
@@ -447,7 +461,8 @@ public:
GuidSet _encounterNPCs;
uint8 _currentRift;
int8 _shieldPercent;
bool _canSpawnPortal;
bool _noBossSpawnDelay;
EventStatus _eventStatus;
TaskScheduler _scheduler;
};
};

View File

@@ -43,9 +43,9 @@ enum medivhMisc
EVENT_OUTRO_8 = 17
};
static std::vector<uint32> firstWave = { NPC_INFINITE_ASSASIN, NPC_INFINITE_WHELP, NPC_INFINITE_CHRONOMANCER };
static std::vector<uint32> secondWave = { NPC_INFINITE_EXECUTIONER, NPC_INFINITE_CHRONOMANCER, NPC_INFINITE_WHELP, NPC_INFINITE_ASSASIN };
static std::vector<uint32> thirdWave = { NPC_INFINITE_EXECUTIONER, NPC_INFINITE_VANQUISHER, NPC_INFINITE_CHRONOMANCER, NPC_INFINITE_ASSASIN };
static std::vector<uint32> firstWave = { NPC_INFINITE_ASSASSIN, NPC_INFINITE_WHELP, NPC_INFINITE_CHRONOMANCER };
static std::vector<uint32> secondWave = { NPC_INFINITE_EXECUTIONER, NPC_INFINITE_CHRONOMANCER, NPC_INFINITE_WHELP, NPC_INFINITE_ASSASSIN };
static std::vector<uint32> thirdWave = { NPC_INFINITE_EXECUTIONER, NPC_INFINITE_VANQUISHER, NPC_INFINITE_CHRONOMANCER, NPC_INFINITE_ASSASSIN };
class NpcRunToHome : public BasicEvent
{
@@ -323,8 +323,8 @@ struct npc_time_rift : public NullCreatureAI
{
switch (entry)
{
case NPC_INFINITE_ASSASIN:
entry = NPC_INFINITE_ASSASIN_2;
case NPC_INFINITE_ASSASSIN:
entry = NPC_INFINITE_ASSASSIN_2;
break;
case NPC_INFINITE_CHRONOMANCER:
entry = NPC_INFINITE_CHRONOMANCER_2;

View File

@@ -36,13 +36,10 @@ enum DataTypes
MAX_ENCOUNTER = 3,
DATA_MEDIVH = 10,
DATA_RIFT_KILLED = 11,
DATA_DAMAGE_SHIELD = 12,
DATA_SHIELD_PERCENT = 13,
DATA_RIFT_NUMBER = 14,
DATA_SUMMONED_NPC = 20,
DATA_DELETED_NPC = 21
DATA_RIFT_NUMBER = 14
};
enum WorldStateIds
@@ -52,6 +49,12 @@ enum WorldStateIds
WORLD_STATE_BM_RIFT = 2784
};
enum EventStatus
{
EVENT_PREPARE = 0,
EVENT_IN_PROGRESS = 1
};
enum QuestIds
{
QUEST_OPENING_PORTAL = 10297,
@@ -75,13 +78,13 @@ enum CreatureIds
NPC_INFINITE_TIMEREAVER = 21698,
NPC_AEONUS = 17881,
NPC_INFINITE_ASSASIN = 17835,
NPC_INFINITE_ASSASSIN = 17835,
NPC_INFINITE_WHELP = 21818,
NPC_INFINITE_CHRONOMANCER = 17892,
NPC_INFINITE_EXECUTIONER = 18994,
NPC_INFINITE_VANQUISHER = 18995,
NPC_INFINITE_ASSASIN_2 = 21137,
NPC_INFINITE_ASSASSIN_2 = 21137,
NPC_INFINITE_CHRONOMANCER_2 = 21136,
NPC_INFINITE_EXECUTIONER_2 = 21138,
NPC_INFINITE_VANQUISHER_2 = 21139,

View File

@@ -44,6 +44,7 @@ enum blySays
enum blySpells
{
SPELL_BLYS_BAND_ESCAPE = 11365,
SPELL_SHIELD_BASH = 11972,
SPELL_REVENGE = 12170
};
@@ -64,6 +65,7 @@ public:
void InitializeAI() override
{
ableToPortHome = false;
startedFight = false;
me->SetFaction(FACTION_FRIENDLY);
postGossipStep = 0;
@@ -74,16 +76,33 @@ public:
InstanceScript* instance;
bool startedFight;
bool ableToPortHome;
uint32 postGossipStep;
uint32 Text_Timer;
uint32 ShieldBash_Timer;
uint32 Revenge_Timer; //this is wrong, spell should never be used unless me->GetVictim() dodge, parry or block attack. Trinity support required.
uint32 Revenge_Timer; //this is wrong, spell should never be used unless me->GetVictim() dodge, parry or block attack. Trinity support required.
uint32 Porthome_Timer;
ObjectGuid PlayerGUID;
void Reset() override
{
ShieldBash_Timer = 5000;
Revenge_Timer = 8000;
Porthome_Timer = 156000;
ableToPortHome = false;
startedFight = false;
}
void EnterEvadeMode(EvadeReason /*reason*/) override
{
if (ableToPortHome)
return;
if (instance->GetData(DATA_PYRAMID) == PYRAMID_KILLED_ALL_TROLLS)
{
ableToPortHome = true;
Porthome_Timer = 156000;
}
}
void MovementInform(uint32 type, uint32 /*id*/) override
@@ -112,6 +131,7 @@ public:
switch (postGossipStep)
{
case 1:
startedFight = true;
//weegli doesn't fight - he goes & blows up the door
if (Creature* pWeegli = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_WEEGLI)))
{
@@ -128,6 +148,7 @@ public:
me->SetFaction(FACTION_MONSTER);
Player* target = ObjectAccessor::GetPlayer(*me, PlayerGUID);
switchFactionIfAlive(NPC_WEEGLI, target);
switchFactionIfAlive(NPC_RAVEN, target);
switchFactionIfAlive(NPC_ORO, target);
switchFactionIfAlive(NPC_MURTA, target);
@@ -146,6 +167,37 @@ public:
}
}
if (Porthome_Timer <= diff && ableToPortHome == true)
{
if (Creature* weegli = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_WEEGLI)))
{
weegli->CastSpell(weegli, SPELL_BLYS_BAND_ESCAPE);
weegli->DespawnOrUnsummon(10000);
}
if (Creature* raven = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_RAVEN)))
{
raven->CastSpell(raven, SPELL_BLYS_BAND_ESCAPE);
raven->DespawnOrUnsummon(10000);
}
if (Creature* oro = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_ORO)))
{
oro->CastSpell(oro, SPELL_BLYS_BAND_ESCAPE);
oro->DespawnOrUnsummon(10000);
}
if (Creature* murta = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_MURTA)))
{
murta->CastSpell(murta, SPELL_BLYS_BAND_ESCAPE);
murta->DespawnOrUnsummon(10000);
}
DoCastSelf(SPELL_BLYS_BAND_ESCAPE);
me->DespawnOrUnsummon(10000);
Porthome_Timer = 156000; //set timer back so that the event doesn't keep triggering
}
else
{
Porthome_Timer -= diff;
}
if (!UpdateVictim())
{
return;
@@ -176,6 +228,7 @@ public:
void DoAction(int32 /*param*/) override
{
ableToPortHome = false;
postGossipStep = 1;
Text_Timer = 0;
}
@@ -211,9 +264,8 @@ public:
void sGossipHello(Player* player) override
{
if (instance->GetData(DATA_PYRAMID) >= PYRAMID_DESTROY_GATES && !startedFight)
if (instance->GetData(DATA_PYRAMID) >= PYRAMID_MOVED_DOWNSTAIRS && !startedFight)
{
startedFight = true;
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_BLY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, 1517, me->GetGUID());
}
@@ -261,6 +313,17 @@ public:
instance->SetData(DATA_PYRAMID, PYRAMID_CAGES_OPEN);
//setting gossip option as soon as the cages open
if(Creature* bly = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BLY)))
{
bly->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}
if(Creature* weegli = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_WEEGLI)))
{
weegli->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}
//set bly & co to aggressive & start moving to top of stairs
initBlyCrewMember(NPC_BLY, 1884.99f, 1263, 41.52f);
initBlyCrewMember(NPC_RAVEN, 1882.5f, 1263, 41.52f);
@@ -281,15 +344,6 @@ public:
crew->GetMotionMaster()->MovePoint(1, { x, y, z, 4.78f });
crew->SetFaction(FACTION_ESCORT_N_NEUTRAL_ACTIVE);
switch (entry)
{
case NPC_BLY:
case NPC_WEEGLI:
crew->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
break;
default:
break;
}
}
}
};
@@ -429,13 +483,13 @@ public:
{
if (instance->GetData(DATA_PYRAMID) == PYRAMID_CAGES_OPEN)
{
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
instance->SetData(DATA_PYRAMID, PYRAMID_ARRIVED_AT_STAIR);
Talk(SAY_WEEGLI_OHNO);
}
else if (instance->GetData(DATA_PYRAMID) == PYRAMID_KILLED_ALL_TROLLS)
else if (instance->GetData(DATA_PYRAMID) >= PYRAMID_KILLED_ALL_TROLLS && instance->GetData(DATA_PYRAMID) < PYRAMID_DESTROY_GATES)
{
instance->SetData(DATA_PYRAMID, PYRAMID_MOVED_DOWNSTAIRS);
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}
else if (instance->GetData(DATA_PYRAMID) == PYRAMID_DESTROY_GATES)
{
@@ -452,13 +506,13 @@ public:
if (instance->GetData(DATA_PYRAMID) == PYRAMID_CAGES_OPEN)
{
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
instance->SetData(DATA_PYRAMID, PYRAMID_ARRIVED_AT_STAIR);
Talk(SAY_WEEGLI_OHNO);
}
else if (instance->GetData(DATA_PYRAMID) == PYRAMID_KILLED_ALL_TROLLS)
else if (instance->GetData(DATA_PYRAMID) >= PYRAMID_KILLED_ALL_TROLLS && instance->GetData(DATA_PYRAMID) < PYRAMID_DESTROY_GATES)
{
instance->SetData(DATA_PYRAMID, PYRAMID_MOVED_DOWNSTAIRS);
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}
else if (instance->GetData(DATA_PYRAMID) == PYRAMID_DESTROY_GATES)
{
@@ -476,10 +530,6 @@ public:
me->SetHomePosition(1858.57f, 1146.35f, 14.745f, 3.85f);
Talk(SAY_WEEGLI_OK_I_GO);
instance->SetData(DATA_PYRAMID, PYRAMID_DESTROY_GATES);
if (Creature* sergeantBly = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BLY)))
{
sergeantBly->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
}
}
}
@@ -501,6 +551,7 @@ public:
switch (instance->GetData(DATA_PYRAMID))
{
case PYRAMID_MOVED_DOWNSTAIRS:
case PYRAMID_KILLED_ALL_TROLLS:
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_WEEGLI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, 1514, me->GetGUID()); //if event can proceed to end
break;
@@ -508,7 +559,7 @@ public:
SendGossipMenuFor(player, 1511, me->GetGUID()); //if event not started
break;
default:
SendGossipMenuFor(player, 1513, me->GetGUID()); //if event are in progress
SendGossipMenuFor(player, 1513, me->GetGUID()); //if event is in progress
}
}
};

View File

@@ -58,17 +58,6 @@ enum Spells
SPELL_STONED = 33652,
};
enum Events
{
EVENT_GROWTH = 1,
EVENT_CAVE_IN = 2,
EVENT_GROUND_SLAM = 3,
EVENT_HURTFUL_STRIKE = 4,
EVENT_REVERBERATION = 5,
EVENT_SHATTER = 6,
EVENT_RECENTLY_SPOKEN = 7
};
struct boss_gruul : public BossAI
{
boss_gruul(Creature* creature) : BossAI(creature, DATA_GRUUL) { }
@@ -76,7 +65,8 @@ struct boss_gruul : public BossAI
void Reset() override
{
_Reset();
_caveInTimer = 29000;
_recentlySpoken = false;
_caveInTimer = 29000ms;
}
void JustEngagedWith(Unit* /*who*/) override
@@ -84,20 +74,61 @@ struct boss_gruul : public BossAI
_JustEngagedWith();
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_GROWTH, 30000);
events.ScheduleEvent(EVENT_CAVE_IN, _caveInTimer);
events.ScheduleEvent(EVENT_REVERBERATION, 20000);
events.ScheduleEvent(EVENT_HURTFUL_STRIKE, 10000);
events.ScheduleEvent(EVENT_GROUND_SLAM, 35000);
scheduler.Schedule(30300ms, [this](TaskContext context)
{
Talk(EMOTE_GROW);
DoCastSelf(SPELL_GROWTH);
context.Repeat(30300ms);
}).Schedule(_caveInTimer, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_CAVE_IN);
if (_caveInTimer > 4000ms)
{
_caveInTimer = _caveInTimer - 1500ms;
}
context.Repeat(_caveInTimer);
}).Schedule(20s, [this](TaskContext context)
{
DoCastSelf(SPELL_REVERBERATION);
context.Repeat(22s);
}).Schedule(10s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 5.0f))
{
DoCast(target, SPELL_HURTFUL_STRIKE);
}
else
{
DoCastVictim(SPELL_HURTFUL_STRIKE);
}
context.Repeat(15s);
}).Schedule(35s, [this](TaskContext context)
{
Talk(SAY_SLAM);
DoCastSelf(SPELL_GROUND_SLAM);
scheduler.DelayAll(9701ms);
scheduler.Schedule(9700ms, [this](TaskContext)
{
Talk(SAY_SHATTER);
me->RemoveAurasDueToSpell(SPELL_LOOK_AROUND);
DoCastSelf(SPELL_SHATTER);
});
context.Repeat(60s);
});
}
void KilledUnit(Unit* /*who*/) override
{
if (events.GetNextEventTime(EVENT_RECENTLY_SPOKEN) == 0)
if (!_recentlySpoken)
{
events.ScheduleEvent(EVENT_RECENTLY_SPOKEN, 5000);
Talk(SAY_SLAY);
_recentlySpoken = true;
}
scheduler.Schedule(5s, [this](TaskContext)
{
_recentlySpoken = false;
});
}
void JustDied(Unit* /*killer*/) override
@@ -111,52 +142,7 @@ struct boss_gruul : public BossAI
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_GROWTH:
Talk(EMOTE_GROW);
DoCast(me, SPELL_GROWTH);
events.ScheduleEvent(EVENT_GROWTH, 30000);
break;
case EVENT_CAVE_IN:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
me->CastSpell(target, SPELL_CAVE_IN, false);
if (_caveInTimer >= 4000)
_caveInTimer -= 1500;
events.ScheduleEvent(EVENT_CAVE_IN, _caveInTimer);
break;
case EVENT_REVERBERATION:
me->CastSpell(me, SPELL_REVERBERATION, false);
events.ScheduleEvent(EVENT_REVERBERATION, 22000);
break;
case EVENT_HURTFUL_STRIKE:
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 5.0f))
{
me->CastSpell(target, SPELL_HURTFUL_STRIKE, false);
}
else
{
me->CastSpell(me->GetVictim(), SPELL_HURTFUL_STRIKE, false);
}
events.ScheduleEvent(EVENT_HURTFUL_STRIKE, 15000);
break;
case EVENT_GROUND_SLAM:
Talk(SAY_SLAM);
me->CastSpell(me, SPELL_GROUND_SLAM, false);
events.DelayEvents(8001);
events.ScheduleEvent(EVENT_GROUND_SLAM, 60000);
events.ScheduleEvent(EVENT_SHATTER, 8000);
break;
case EVENT_SHATTER:
Talk(SAY_SHATTER);
me->RemoveAurasDueToSpell(SPELL_LOOK_AROUND);
me->CastSpell(me, SPELL_SHATTER, false);
break;
}
scheduler.Update(diff);
if (!me->HasUnitState(UNIT_STATE_ROOT))
{
@@ -165,7 +151,8 @@ struct boss_gruul : public BossAI
}
private:
uint32 _caveInTimer;
std::chrono::milliseconds _caveInTimer;
bool _recentlySpoken;
};
struct npc_invisible_tractor_beam_source : public NullCreatureAI

View File

@@ -17,6 +17,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "TaskScheduler.h"
#include "gruuls_lair.h"
enum HighKingMaulgar
@@ -59,39 +60,49 @@ enum HighKingMaulgar
ACTION_ADD_DEATH = 1
};
enum HKMEvents
{
EVENT_RECENTLY_SPOKEN = 1,
EVENT_ARCING_SMASH = 2,
EVENT_MIGHTY_BLOW = 3,
EVENT_WHIRLWIND = 4,
EVENT_CHARGING = 5,
EVENT_ROAR = 6,
EVENT_CHECK_HEALTH = 7,
EVENT_ADD_ABILITY1 = 10,
EVENT_ADD_ABILITY2 = 11,
EVENT_ADD_ABILITY3 = 12,
EVENT_ADD_ABILITY4 = 13
};
struct boss_high_king_maulgar : public BossAI
{
boss_high_king_maulgar(Creature* creature) : BossAI(creature, DATA_MAULGAR) { }
boss_high_king_maulgar(Creature* creature) : BossAI(creature, DATA_MAULGAR)
{
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
_Reset();
_recentlySpoken = false;
me->SetLootMode(0);
ScheduleHealthCheckEvent(50, [&]{
Talk(SAY_ENRAGE);
DoCastSelf(SPELL_FLURRY);
scheduler.Schedule(0ms, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_BERSERKER_C);
context.Repeat(35s);
}).Schedule(0ms, [this](TaskContext context)
{
DoCastSelf(SPELL_ROAR);
context.Repeat(20600ms, 29100ms);
});
});
}
void KilledUnit(Unit* /*victim*/) override
{
if (events.GetNextEventTime(EVENT_RECENTLY_SPOKEN) == 0)
if(!_recentlySpoken)
{
events.ScheduleEvent(EVENT_RECENTLY_SPOKEN, 5s);
Talk(SAY_SLAY);
_recentlySpoken = true;
}
scheduler.Schedule(5s, [this](TaskContext)
{
_recentlySpoken = false;
});
}
void JustDied(Unit* /*killer*/) override
@@ -126,10 +137,20 @@ struct boss_high_king_maulgar : public BossAI
_JustEngagedWith();
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_ARCING_SMASH, 10s);
events.ScheduleEvent(EVENT_MIGHTY_BLOW, 15s);
events.ScheduleEvent(EVENT_WHIRLWIND, 54s);
events.ScheduleEvent(EVENT_CHECK_HEALTH, 500ms);
scheduler.Schedule(9500ms, [this](TaskContext context)
{
DoCastVictim(SPELL_ARCING_SMASH);
context.Repeat(9500ms, 12s);
}).Schedule(15700ms, [this](TaskContext context)
{
DoCastVictim(SPELL_MIGHTY_BLOW);
context.Repeat(16200ms, 19s);
}).Schedule(67000ms, [this](TaskContext context)
{
scheduler.DelayAll(15s);
DoCastSelf(SPELL_WHIRLWIND);
context.Repeat(45s, 60s);
});
}
void UpdateAI(uint32 diff) override
@@ -137,49 +158,12 @@ struct boss_high_king_maulgar : public BossAI
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_ARCING_SMASH:
me->CastSpell(me->GetVictim(), SPELL_ARCING_SMASH, false);
events.ScheduleEvent(EVENT_ARCING_SMASH, 10s);
break;
case EVENT_MIGHTY_BLOW:
me->CastSpell(me->GetVictim(), SPELL_MIGHTY_BLOW, false);
events.ScheduleEvent(EVENT_MIGHTY_BLOW, 15s);
break;
case EVENT_WHIRLWIND:
events.DelayEvents(15s);
me->CastSpell(me, SPELL_WHIRLWIND, false);
events.ScheduleEvent(EVENT_WHIRLWIND, 54s);
break;
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(50))
{
Talk(SAY_ENRAGE);
me->CastSpell(me, SPELL_FLURRY, true);
events.ScheduleEvent(EVENT_CHARGING, 0s);
events.ScheduleEvent(EVENT_ROAR, 30s);
break;
}
events.ScheduleEvent(EVENT_CHECK_HEALTH, 500ms);
break;
case EVENT_ROAR:
me->CastSpell(me, SPELL_ROAR, false);
events.ScheduleEvent(EVENT_ROAR, 40s);
break;
case EVENT_CHARGING:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
me->CastSpell(target, SPELL_BERSERKER_C, false);
events.ScheduleEvent(EVENT_CHARGING, 35s);
break;
}
scheduler.Update(diff);
DoMeleeAttackIfReady();
}
private:
bool _recentlySpoken;
};
struct boss_olm_the_summoner : public ScriptedAI
@@ -187,15 +171,18 @@ struct boss_olm_the_summoner : public ScriptedAI
boss_olm_the_summoner(Creature* creature) : ScriptedAI(creature), summons(me)
{
instance = creature->GetInstanceScript();
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
EventMap events;
SummonList summons;
InstanceScript* instance;
void Reset() override
{
events.Reset();
_scheduler.CancelAll();
summons.DespawnAll();
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
@@ -214,9 +201,22 @@ struct boss_olm_the_summoner : public ScriptedAI
me->SetInCombatWithZone();
instance->SetBossState(DATA_MAULGAR, IN_PROGRESS);
events.ScheduleEvent(EVENT_ADD_ABILITY1, 500ms);
events.ScheduleEvent(EVENT_ADD_ABILITY2, 5s);
events.ScheduleEvent(EVENT_ADD_ABILITY3, 6500ms);
_scheduler.Schedule(1200ms, [this](TaskContext context)
{
DoCastSelf(SPELL_SUMMON_WFH);
context.Repeat(48500ms);
}).Schedule(6050ms, [this](TaskContext context)
{
DoCastVictim(SPELL_DARK_DECAY);
context.Repeat(6050ms);
}).Schedule(6500ms, [this](TaskContext context)
{
if (me->HealthBelowPct(90))
{
DoCastRandomTarget(SPELL_DEATH_COIL);
}
context.Repeat(6s, 13500ms);
});
}
void JustDied(Unit* /*killer*/) override
@@ -234,27 +234,12 @@ struct boss_olm_the_summoner : public ScriptedAI
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
_scheduler.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_ADD_ABILITY1:
me->CastSpell(me, SPELL_SUMMON_WFH, false);
events.ScheduleEvent(EVENT_ADD_ABILITY1, 50s);
break;
case EVENT_ADD_ABILITY2:
DoCastVictim(SPELL_DARK_DECAY);
events.ScheduleEvent(EVENT_ADD_ABILITY2, 6500ms);
break;
case EVENT_ADD_ABILITY3:
DoCastRandomTarget(SPELL_DEATH_COIL);
events.ScheduleEvent(EVENT_ADD_ABILITY3, 7s);
break;
}
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
};
struct boss_kiggler_the_crazed : public ScriptedAI
@@ -262,14 +247,17 @@ struct boss_kiggler_the_crazed : public ScriptedAI
boss_kiggler_the_crazed(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
EventMap events;
InstanceScript* instance;
void Reset() override
{
events.Reset();
_scheduler.CancelAll();
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
@@ -278,10 +266,35 @@ struct boss_kiggler_the_crazed : public ScriptedAI
me->SetInCombatWithZone();
instance->SetBossState(DATA_MAULGAR, IN_PROGRESS);
events.ScheduleEvent(EVENT_ADD_ABILITY1, 1500ms);
events.ScheduleEvent(EVENT_ADD_ABILITY2, 5s);
events.ScheduleEvent(EVENT_ADD_ABILITY3, 25s);
events.ScheduleEvent(EVENT_ADD_ABILITY4, 30s);
_scheduler.Schedule(1200ms, [this](TaskContext context)
{
DoCastVictim(SPELL_LIGHTNING_BOLT);
context.Repeat(2400ms);
}).Schedule(29s, [this](TaskContext context)
{
DoCastVictim(SPELL_ARCANE_SHOCK);
context.Repeat(7200ms, 20600ms);
}).Schedule(23s, [this](TaskContext context)
{
//changed to work similarly to Ikiss poly
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_GREATER_POLYMORPH);
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, [&]
(Unit* target) -> bool
{
return target && !target->IsImmunedToSpell(spellInfo);
}))
{
DoCast(target, SPELL_GREATER_POLYMORPH);
}
context.Repeat(10900ms);
}).Schedule(30s, [this](TaskContext context)
{
if (me->SelectNearestPlayer(30.0f))
{
DoCastAOE(SPELL_ARCANE_EXPLOSION);
}
context.Repeat(30s);
});
}
void JustDied(Unit* /*killer*/) override
@@ -294,36 +307,12 @@ struct boss_kiggler_the_crazed : public ScriptedAI
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_ADD_ABILITY1:
DoCastVictim(SPELL_LIGHTNING_BOLT);
events.ScheduleEvent(EVENT_ADD_ABILITY1, 1500ms);
break;
case EVENT_ADD_ABILITY2:
DoCastVictim(SPELL_ARCANE_SHOCK);
events.ScheduleEvent(EVENT_ADD_ABILITY2, 5s);
break;
case EVENT_ADD_ABILITY3:
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1)) //target method should perhaps change
me->CastSpell(target, SPELL_GREATER_POLYMORPH, false);
events.ScheduleEvent(EVENT_ADD_ABILITY3, 11s);
break;
case EVENT_ADD_ABILITY4:
if (me->SelectNearestPlayer(30.0f))
{
DoCastAOE(SPELL_ARCANE_EXPLOSION);
}
events.ScheduleEvent(EVENT_ADD_ABILITY4, 30s);
break;
}
_scheduler.Update(diff);
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
};
struct boss_blindeye_the_seer : public ScriptedAI
@@ -331,14 +320,17 @@ struct boss_blindeye_the_seer : public ScriptedAI
boss_blindeye_the_seer(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
EventMap events;
InstanceScript* instance;
void Reset() override
{
events.Reset();
_scheduler.CancelAll();
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
@@ -347,9 +339,22 @@ struct boss_blindeye_the_seer : public ScriptedAI
me->SetInCombatWithZone();
instance->SetBossState(DATA_MAULGAR, IN_PROGRESS);
events.ScheduleEvent(EVENT_ADD_ABILITY1, 11s);
events.ScheduleEvent(EVENT_ADD_ABILITY2, 30s);
events.ScheduleEvent(EVENT_ADD_ABILITY3, 31s);
_scheduler.Schedule(7200ms, [this](TaskContext context)
{
if (Unit* target = DoSelectLowestHpFriendly(60.0f, 50000))
{
DoCast(target, SPELL_HEAL);
}
context.Repeat(7200ms);
}).Schedule(37500s, [this](TaskContext context)
{
DoCastSelf(SPELL_GREATER_PW_SHIELD);
_scheduler.Schedule(1200ms, [this](TaskContext)
{
DoCastSelf(SPELL_PRAYER_OH);
});
context.Repeat(54500ms, 63s);
});
}
void JustDied(Unit* /*killer*/) override
@@ -362,31 +367,12 @@ struct boss_blindeye_the_seer : public ScriptedAI
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_ADD_ABILITY1:
if (Unit* target = DoSelectLowestHpFriendly(60.0f, 50000))
{
DoCast(target, SPELL_HEAL);
}
events.ScheduleEvent(EVENT_ADD_ABILITY1, 6s);
break;
case EVENT_ADD_ABILITY2:
DoCastSelf(SPELL_GREATER_PW_SHIELD);
events.ScheduleEvent(EVENT_ADD_ABILITY2, 30s);
break;
case EVENT_ADD_ABILITY3:
me->CastSpell(me, SPELL_PRAYER_OH, false);
events.ScheduleEvent(EVENT_ADD_ABILITY3, 30s);
break;
}
_scheduler.Update(diff);
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
};
struct boss_krosh_firehand : public ScriptedAI
@@ -394,14 +380,17 @@ struct boss_krosh_firehand : public ScriptedAI
boss_krosh_firehand(Creature* creature) : ScriptedAI(creature)
{
instance = creature->GetInstanceScript();
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
EventMap events;
InstanceScript* instance;
void Reset() override
{
events.Reset();
_scheduler.CancelAll();
instance->SetBossState(DATA_MAULGAR, NOT_STARTED);
}
@@ -419,9 +408,22 @@ struct boss_krosh_firehand : public ScriptedAI
me->SetInCombatWithZone();
instance->SetBossState(DATA_MAULGAR, IN_PROGRESS);
events.ScheduleEvent(EVENT_ADD_ABILITY1, 1500ms); //spellshield
events.ScheduleEvent(EVENT_ADD_ABILITY2, 3500ms); //greater fireball
events.ScheduleEvent(EVENT_ADD_ABILITY3, 8s); //blast wave (needs to check for players in range)
_scheduler.Schedule(1200ms, [this](TaskContext context)
{
DoCastSelf(SPELL_SPELLSHIELD);
context.Repeat(30300ms);
}).Schedule(3600ms, [this](TaskContext context)
{
DoCastVictim(SPELL_GREATER_FIREBALL);
context.Repeat(3600ms);
}).Schedule(7200ms, [this](TaskContext context)
{
if (me->SelectNearestPlayer(15.0f))
{
DoCastAOE(SPELL_BLAST_WAVE);
}
context.Repeat(7200ms);
});
}
void JustDied(Unit* /*killer*/) override
@@ -434,31 +436,12 @@ struct boss_krosh_firehand : public ScriptedAI
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_ADD_ABILITY1:
DoCastSelf(SPELL_SPELLSHIELD);
events.ScheduleEvent(EVENT_ADD_ABILITY1, 30s);
break;
case EVENT_ADD_ABILITY2:
DoCastVictim(SPELL_GREATER_FIREBALL);
events.ScheduleEvent(EVENT_ADD_ABILITY2, 1s);
break;
case EVENT_ADD_ABILITY3:
if (me->SelectNearestPlayer(15.0f))
{
DoCastAOE(SPELL_BLAST_WAVE);
}
events.ScheduleEvent(EVENT_ADD_ABILITY3, 8s);
break;
}
_scheduler.Update(diff);
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
};
void AddSC_boss_high_king_maulgar()

View File

@@ -144,7 +144,28 @@ private:
};
class spell_gargolmar_retalliation : public AuraScript
{
PrepareAuraScript(spell_gargolmar_retalliation);
bool CheckProc(ProcEventInfo& eventInfo)
{
if (!eventInfo.GetActor() || !eventInfo.GetProcTarget())
{
return false;
}
return GetTarget()->isInFront(eventInfo.GetActor(), M_PI);
}
void Register() override
{
DoCheckProc += AuraCheckProcFn(spell_gargolmar_retalliation::CheckProc);
}
};
void AddSC_boss_watchkeeper_gargolmar()
{
RegisterHellfireRampartsCreatureAI(boss_watchkeeper_gargolmar);
RegisterSpellScript(spell_gargolmar_retalliation);
}