fix(Scripts/TempleOfAhnQiraj): Rewrite C'thun tentacles (#12047)

This commit is contained in:
Skjalf
2022-06-14 09:24:14 -03:00
committed by GitHub
parent d3bd763dc5
commit 987d49f9ba
@@ -25,38 +25,9 @@ EndScriptData */
#include "Player.h" #include "Player.h"
#include "ScriptMgr.h" #include "ScriptMgr.h"
#include "ScriptedCreature.h" #include "ScriptedCreature.h"
#include "TaskScheduler.h"
#include "temple_of_ahnqiraj.h" #include "temple_of_ahnqiraj.h"
/*
* This is a 2 phases events. Here follows an explanation of the main events and transition between phases and sub-phases.
*
* The first phase is the EYE phase: the Eye of C'Thun is active and C'thun is not active.
* During this phase, the "Eye of C'Thun" alternates between 2 sub-phases:
* - PHASE_EYE_GREEN_BEAM:
* 50 sec phase during which the Eye mainly casts its Green Beam every 3 sec.
* - PHASE_EYE_RED_BEAM:
* 35 sec phase during which the Eye casts its red beam every sec.
* This EYE phase ends when the "Eye of C'Thun" is killed. Then starts the CTHUN phase.
*
* The second phase is the CTHUN phase. The Eye of C'Thun is not active and C'Thun is active.
* This phase starts with the transformation of the Eye into C'Thun (PHASE_CTHUN_TRANSITION).
* After the transformation, C'Thun alternates between 2 sub-phases:
* - PHASE_CTHUN_STOMACH:
* - C'Thun is almost insensible to all damage (99% damage reduction).
* - It spawns 2 tentacles in its stomach.
* - C'Thun swallows players.
* - This sub-phase ends when the 2 tentacles are killed. Swallowed players are regurgitate.
*
* - PHASE_CTHUN_WEAK:
* - weakened C'Thun takes normal damage.
* - This sub-phase ends after 45 secs.
*
* This CTHUN phase ends when C'Thun is killed
*
* Note:
* - the current phase is stored in the instance data to be easily shared between the eye and cthun.
*/
enum Phases enum Phases
{ {
PHASE_NOT_STARTED = 0, PHASE_NOT_STARTED = 0,
@@ -99,7 +70,7 @@ enum Spells
//SAME AS PHASE1 //SAME AS PHASE1
//Giant Claw Tentacles //Giant Claw Tentacles
SPELL_MASSIVE_GROUND_RUPTURE = 26100, SPELL_MASSIVE_GROUND_RUPTURE = 26478,
//Also casts Hamstring //Also casts Hamstring
SPELL_THRASH = 3391, SPELL_THRASH = 3391,
@@ -111,6 +82,10 @@ enum Spells
SPELL_MOUTH_TENTACLE = 26332, SPELL_MOUTH_TENTACLE = 26332,
SPELL_EXIT_STOMACH_KNOCKBACK = 25383, SPELL_EXIT_STOMACH_KNOCKBACK = 25383,
SPELL_DIGESTIVE_ACID = 26476, SPELL_DIGESTIVE_ACID = 26476,
// Tentacles
SPELL_SUBMERGE_VISUAL = 26234,
SPELL_BIRTH = 26262
}; };
enum Actions enum Actions
@@ -142,6 +117,20 @@ const Position FleshTentaclePos[2] =
{ -8525.0f, 1994.0f, -98.0f, 2.12f}, { -8525.0f, 1994.0f, -98.0f, 2.12f},
}; };
class NotInStomachSelector
{
public:
NotInStomachSelector() { }
bool operator()(Unit* unit) const
{
if (unit->GetTypeId() != TYPEID_PLAYER || unit->HasAura(SPELL_DIGESTIVE_ACID))
return false;
return true;
}
};
//Kick out position //Kick out position
const Position KickPos = { -8545.0f, 1984.0f, -96.0f, 0.0f}; const Position KickPos = { -8545.0f, 1984.0f, -96.0f, 0.0f};
@@ -917,32 +906,40 @@ public:
{ {
eye_tentacleAI(Creature* creature) : ScriptedAI(creature) eye_tentacleAI(Creature* creature) : ScriptedAI(creature)
{ {
if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) if (Creature* portal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
{ {
pPortal->SetReactState(REACT_PASSIVE); portal->SetReactState(REACT_PASSIVE);
Portal = pPortal->GetGUID(); _portalGUID = portal->GetGUID();
} }
SetCombatMovement(false); SetCombatMovement(false);
} }
uint32 MindflayTimer;
uint32 KillSelfTimer;
ObjectGuid Portal;
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
{ {
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID))
{
Unit::Kill(p, p); Unit::Kill(p, p);
}
} }
void Reset() override void Reset() override
{ {
//Mind flay half a second after we spawn _scheduler.Schedule(500ms, [this](TaskContext /*task*/)
MindflayTimer = 500; {
DoCastAOE(SPELL_GROUND_RUPTURE);
}).Schedule(5min, [this](TaskContext /*task*/)
{
me->DespawnOrUnsummon();
}).Schedule(1s, 5s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, [&](Unit* u) { return u && u->GetTypeId() == TYPEID_PLAYER && !u->HasAura(SPELL_DIGESTIVE_ACID) && !u->HasAura(SPELL_MIND_FLAY); }))
{
DoCast(target, SPELL_MIND_FLAY);
}
//This prevents eyes from overlapping context.Repeat(10s, 15s);
KillSelfTimer = 35000; });
} }
void EnterCombat(Unit* /*who*/) override void EnterCombat(Unit* /*who*/) override
@@ -956,26 +953,12 @@ public:
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
//KillSelfTimer _scheduler.Update(diff);
if (KillSelfTimer <= diff)
{
Unit::Kill(me, me);
return;
}
else KillSelfTimer -= diff;
//MindflayTimer
if (MindflayTimer <= diff)
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
if (target && !target->HasAura(SPELL_DIGESTIVE_ACID))
DoCast(target, SPELL_MIND_FLAY);
//Mindflay every 10 seconds
MindflayTimer = 10000;
}
else MindflayTimer -= diff;
} }
private:
TaskScheduler _scheduler;
ObjectGuid _portalGUID;
}; };
}; };
@@ -995,35 +978,41 @@ public:
{ {
SetCombatMovement(false); SetCombatMovement(false);
if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) if (Creature* portal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
{ {
pPortal->SetReactState(REACT_PASSIVE); portal->SetReactState(REACT_PASSIVE);
Portal = pPortal->GetGUID(); _portalGUID = portal->GetGUID();
} }
} }
uint32 GroundRuptureTimer;
uint32 HamstringTimer;
uint32 EvadeTimer;
ObjectGuid Portal;
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
{ {
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID))
{
Unit::Kill(p, p); Unit::Kill(p, p);
}
} }
void Reset() override void Reset() override
{ {
//First rupture should happen half a second after we spawn _scheduler.Schedule(Milliseconds(500), [this](TaskContext /*task*/)
GroundRuptureTimer = 500; {
HamstringTimer = 2000; DoCastAOE(SPELL_GROUND_RUPTURE);
EvadeTimer = 5000; }).Schedule(Minutes(5), [this](TaskContext /*task*/)
{
me->DespawnOrUnsummon();
});
} }
void EnterCombat(Unit* /*who*/) override void EnterCombat(Unit* /*who*/) override
{ {
DoZoneInCombat(); DoZoneInCombat();
_scheduler.Schedule(2s, [this](TaskContext context)
{
DoCastVictim(SPELL_HAMSTRING);
context.Repeat(5s);
});
} }
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
@@ -1032,62 +1021,14 @@ public:
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
//EvadeTimer _scheduler.Update(diff);
if (!me->IsWithinMeleeRange(me->GetVictim()))
{
if (EvadeTimer <= diff)
{
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
Unit::Kill(p, p);
//Dissapear and reappear at new position
me->SetVisible(false);
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
if (!target)
{
Unit::Kill(me, me);
return;
}
if (!target->HasAura(SPELL_DIGESTIVE_ACID))
{
me->SetPosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0);
if (Creature* pPortal = me->SummonCreature(NPC_SMALL_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
{
pPortal->SetReactState(REACT_PASSIVE);
Portal = pPortal->GetGUID();
}
GroundRuptureTimer = 500;
HamstringTimer = 2000;
EvadeTimer = 5000;
AttackStart(target);
}
me->SetVisible(true);
}
else EvadeTimer -= diff;
}
//GroundRuptureTimer
if (GroundRuptureTimer <= diff)
{
DoCastVictim(SPELL_GROUND_RUPTURE);
GroundRuptureTimer = 30000;
}
else GroundRuptureTimer -= diff;
//HamstringTimer
if (HamstringTimer <= diff)
{
DoCastVictim(SPELL_HAMSTRING);
HamstringTimer = 5000;
}
else HamstringTimer -= diff;
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
private:
TaskScheduler _scheduler;
ObjectGuid _portalGUID;
}; };
}; };
@@ -1107,37 +1048,112 @@ public:
{ {
SetCombatMovement(false); SetCombatMovement(false);
if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
{ {
pPortal->SetReactState(REACT_PASSIVE); portal->SetReactState(REACT_PASSIVE);
Portal = pPortal->GetGUID(); _portalGUID = portal->GetGUID();
} }
} }
uint32 GroundRuptureTimer;
uint32 ThrashTimer;
uint32 HamstringTimer;
uint32 EvadeTimer;
ObjectGuid Portal;
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
{ {
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID))
{
Unit::Kill(p, p); Unit::Kill(p, p);
}
} }
void Reset() override void Reset() override
{ {
//First rupture should happen half a second after we spawn _scheduler.Schedule(500ms, [this](TaskContext /*task*/)
GroundRuptureTimer = 500; {
HamstringTimer = 2000; DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE);
ThrashTimer = 5000; });
EvadeTimer = 5000;
} }
void EnterCombat(Unit* /*who*/) override void EnterCombat(Unit* /*who*/) override
{ {
DoZoneInCombat(); DoZoneInCombat();
_scheduler.Schedule(2s, [this](TaskContext context)
{
DoCastVictim(SPELL_HAMSTRING);
context.Repeat(10s);
}).Schedule(5s, [this](TaskContext context) {
DoCastSelf(SPELL_THRASH);
context.Repeat(10s);
});
}
void ScheduleMeleeCheck()
{
// Check if a target is in melee range
_scheduler.Schedule(10s, [this](TaskContext task)
{
if (Unit* target = me->GetVictim())
{
if (!target->IsWithinMeleeRange(me))
{
// Main target not found within melee range, try to select a new one
if (Player* newTarget = me->SelectNearestPlayer(5.0f))
{
AttackStart(newTarget);
}
else // Main target not found, and failed to acquire a new target... Submerge
{
Submerge();
}
}
}
task.Repeat();
});
}
void Submerge()
{
if (me->SelectNearestPlayer(5.0f))
{
return;
}
// Despawn portal
if (Creature* p = ObjectAccessor::GetCreature(*me, _portalGUID))
{
p->DespawnOrUnsummon();
}
DoCastSelf(SPELL_SUBMERGE_VISUAL);
me->SetHealth(me->GetMaxHealth());
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
_scheduler.CancelAll();
_scheduler.Schedule(5s, [this](TaskContext /*task*/)
{
Emerge();
});
}
void Emerge()
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, NotInStomachSelector()))
{
Position pos = target->GetPosition();
me->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), 0);
if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, pos, TEMPSUMMON_CORPSE_DESPAWN))
{
portal->SetReactState(REACT_PASSIVE);
_portalGUID = portal->GetGUID();
}
me->RemoveAurasDueToSpell(SPELL_SUBMERGE_VISUAL);
DoCastSelf(SPELL_BIRTH);
DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE, true);
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
ScheduleMeleeCheck();
}
} }
void UpdateAI(uint32 diff) override void UpdateAI(uint32 diff) override
@@ -1146,70 +1162,14 @@ public:
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
//EvadeTimer _scheduler.Update(diff);
if (!me->IsWithinMeleeRange(me->GetVictim()))
{
if (EvadeTimer <= diff)
{
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
Unit::Kill(p, p);
//Dissapear and reappear at new position
me->SetVisible(false);
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
if (!target)
{
Unit::Kill(me, me);
return;
}
if (!target->HasAura(SPELL_DIGESTIVE_ACID))
{
me->SetPosition(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0);
if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
{
pPortal->SetReactState(REACT_PASSIVE);
Portal = pPortal->GetGUID();
}
GroundRuptureTimer = 500;
HamstringTimer = 2000;
ThrashTimer = 5000;
EvadeTimer = 5000;
AttackStart(target);
}
me->SetVisible(true);
}
else EvadeTimer -= diff;
}
//GroundRuptureTimer
if (GroundRuptureTimer <= diff)
{
DoCastVictim(SPELL_GROUND_RUPTURE);
GroundRuptureTimer = 30000;
}
else GroundRuptureTimer -= diff;
//ThrashTimer
if (ThrashTimer <= diff)
{
DoCastVictim(SPELL_THRASH);
ThrashTimer = 10000;
}
else ThrashTimer -= diff;
//HamstringTimer
if (HamstringTimer <= diff)
{
DoCastVictim(SPELL_HAMSTRING);
HamstringTimer = 10000;
}
else HamstringTimer -= diff;
DoMeleeAttackIfReady(); DoMeleeAttackIfReady();
} }
private:
TaskScheduler _scheduler;
ObjectGuid _portalGUID;
}; };
}; };
@@ -1229,26 +1189,34 @@ public:
{ {
SetCombatMovement(false); SetCombatMovement(false);
if (Creature* pPortal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN)) if (Creature* portal = me->SummonCreature(NPC_GIANT_PORTAL, *me, TEMPSUMMON_CORPSE_DESPAWN))
{ {
pPortal->SetReactState(REACT_PASSIVE); portal->SetReactState(REACT_PASSIVE);
Portal = pPortal->GetGUID(); _portalGUID = portal->GetGUID();
} }
} }
uint32 BeamTimer;
ObjectGuid Portal;
void JustDied(Unit* /*killer*/) override void JustDied(Unit* /*killer*/) override
{ {
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal)) if (Unit* p = ObjectAccessor::GetUnit(*me, _portalGUID))
{
Unit::Kill(p, p); Unit::Kill(p, p);
}
} }
void Reset() override void Reset() override
{ {
//Green Beam half a second after we spawn _scheduler.Schedule(500ms, [this](TaskContext /*task*/)
BeamTimer = 500; {
DoCastAOE(SPELL_MASSIVE_GROUND_RUPTURE);
}).Schedule(1s, 5s, [this](TaskContext context) {
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, -SPELL_DIGESTIVE_ACID))
{
DoCast(target, SPELL_GREEN_BEAM);
}
context.Repeat(2100ms);
});
} }
void EnterCombat(Unit* /*who*/) override void EnterCombat(Unit* /*who*/) override
@@ -1262,18 +1230,12 @@ public:
if (!UpdateVictim()) if (!UpdateVictim())
return; return;
//BeamTimer _scheduler.Update(diff);
if (BeamTimer <= diff)
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0);
if (target && !target->HasAura(SPELL_DIGESTIVE_ACID))
DoCast(target, SPELL_GREEN_BEAM);
//Beam every 2 seconds
BeamTimer = 2100;
}
else BeamTimer -= diff;
} }
private:
TaskScheduler _scheduler;
ObjectGuid _portalGUID;
}; };
}; };