fix(Core/TempleOfAhnQiraj): Twin emperors rewrite (#12855)

This commit is contained in:
Angelo Venturini
2022-09-06 21:33:51 -03:00
committed by GitHub
parent 25d4d91867
commit fa825ec419
6 changed files with 438 additions and 523 deletions

View File

@@ -0,0 +1,43 @@
--
UPDATE `creature_template_addon` SET `bytes1` = 54432 WHERE `entry` = 15963;
UPDATE `creature_template_addon` SET `auras` = '18943' WHERE `entry` = 15275;
DELETE FROM `areatrigger_scripts` WHERE `ScriptName` = 'at_twin_emperors';
INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES
(4047, 'at_twin_emperors');
DELETE FROM `creature_text` WHERE `CreatureID` IN (15275, 15276) AND `GroupID` IN (0, 1, 2, 3, 4, 5, 6);
DELETE FROM `creature_text` WHERE `CreatureID` = 15963;
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(15275, 0, 0, 'Where are your manners, brother. Let us properly welcome our guests.', 14, 0, 100, 6, 0, 0, 11706, 0, 'Emperor Vek\'nilash INTRO_0'),
(15275, 1, 0, 'Oh so much pain...', 14, 0, 100, 5, 0, 0, 11708, 0, 'Emperor Vek\'nilash INTRO_1'),
(15275, 2, 0, 'The feast of souls begins now...', 14, 0, 100, 0, 0, 0, 11710, 0, 'Emperor Vek\'nilash INTRO_2'),
(15275, 3, 0, 'Your fate is sealed!', 14, 0, 100, 0, 0, 8635, 11455, 0, 'Emperor Vek\'nilash KILL'),
(15275, 4, 0, 'Vek\'lor, I feel your pain!', 14, 0, 100, 0, 0, 8636, 11454, 0, 'Emperor Vek\'nilash DEATH'),
(15276, 0, 0, 'Only flesh and bone. Mortals are such easy prey...', 14, 0, 100, 1, 0, 0, 11702, 0, 'Emperor Vek\'lor INTRO_0'),
(15276, 1, 0, 'There will be pain...', 14, 0, 100, 273, 0, 0, 11707, 0, 'Emperor Vek\'lor INTRO_1'),
(15276, 2, 0, 'Come, little ones.', 14, 0, 100, 0, 0, 0, 11709, 0, 'Emperor Vek\'lor INTRO_2'),
(15276, 3, 0, 'You will not escape death!', 14, 0, 100, 0, 0, 8629, 11453, 0, 'Emperor Vek\'lor KILL'),
(15276, 4, 0, 'My brother, no!', 14, 0, 100, 0, 0, 8625, 11452, 0, 'Emperor Vek\'lor DEATH'),
(15276, 5, 0, '%s goes into a berserker rage!', 16, 0, 100, 0, 0, 0, 34057, 0, 'Emperor Vek\'lor ENRAGE'),
(15963, 0, 0, 'The massive floating eyeball in the center of the chamber turns its gaze upon you. You stand before a god.', 16, 0, 100, 0, 0, 0, 11700, 1, 'Masters Eye - On Areatrigger');
UPDATE `creature_template_movement` SET `Ground` = 2, `Flight` = 1 WHERE `CreatureId` = 15963;
UPDATE `creature_template` SET `unit_flags` = `unit_flags`|512|256 WHERE `entry` = 15963;
UPDATE `creature_template` SET `spell_school_immune_mask` = `spell_school_immune_mask`|1 WHERE `entry` = 15276; -- Vek'lor immunity to phys
UPDATE `creature_template` SET `spell_school_immune_mask` = `spell_school_immune_mask`|4|8|16|32|64 WHERE `entry` = 15275; -- Vek'nilash immunity to everything but holy and phys
DELETE FROM `spell_script_names` WHERE `spell_id` IN (802, 804);
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(802, 'spell_mutate_explode_bug'),
(804, 'spell_mutate_explode_bug');
UPDATE `creature` SET `spawntimesecs` = 300 WHERE `id1` IN (15316, 15317);
UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` IN (15316, 15317);
DELETE FROM `smart_scripts` WHERE (`entryorguid` IN (15316, 15317)) AND (`source_type` = 0) AND (`id` IN (0));
INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
(15317, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 2, 311, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Qiraji Scorpion - On Reset - Set Faction 311'),
(15316, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 2, 311, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Qiraji Scarab - On Reset - Set Faction 311');

View File

@@ -19518,6 +19518,11 @@ bool Unit::CanSwim() const
return HasUnitFlag(UNIT_FLAG_RENAME | UNIT_FLAG_SWIMMING);
}
void Unit::NearTeleportTo(Position& pos, bool casting /*= false*/, bool vehicleTeleport /*= false*/, bool withPet /*= false*/, bool removeTransport /*= false*/)
{
NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), casting, vehicleTeleport, withPet, removeTransport);
}
void Unit::NearTeleportTo(float x, float y, float z, float orientation, bool casting /*= false*/, bool vehicleTeleport /*= false*/, bool withPet /*= false*/, bool removeTransport /*= false*/)
{
DisableSpline();

View File

@@ -1731,6 +1731,7 @@ public:
void SendSpellDamageResist(Unit* target, uint32 spellId);
void SendSpellDamageImmune(Unit* target, uint32 spellId);
void NearTeleportTo(Position& pos, bool casting = false, bool vehicleTeleport = false, bool withPet = false, bool removeTransport = false);
void NearTeleportTo(float x, float y, float z, float orientation, bool casting = false, bool vehicleTeleport = false, bool withPet = false, bool removeTransport = false);
void SendTameFailure(uint8 result);
void SendTeleportPacket(Position& pos);

View File

@@ -15,368 +15,283 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Boss_Twinemperors
SD%Complete: 95
SDComment:
SDCategory: Temple of Ahn'Qiraj
EndScriptData */
#include "Item.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "Spell.h"
#include "WorldPacket.h"
#include "SpellScript.h"
#include "TaskScheduler.h"
#include "temple_of_ahnqiraj.h"
enum Spells
{
// Both
SPELL_TWIN_EMPATHY = 1177,
SPELL_TWIN_TELEPORT_1 = 800,
SPELL_TWIN_TELEPORT_VISUAL = 26638,
SPELL_HEAL_BROTHER = 7393,
SPELL_TWIN_TELEPORT = 800, // CTRA watches for this spell to start its teleport timer
SPELL_TWIN_TELEPORT_VISUAL = 26638, // visual
SPELL_EXPLODEBUG = 804,
SPELL_MUTATE_BUG = 802,
SPELL_BERSERK = 26662,
// Vek'lor
SPELL_SHADOW_BOLT = 26006,
SPELL_BLIZZARD = 26607,
SPELL_FRENZY = 27897,
SPELL_ARCANE_BURST = 568,
SPELL_EXPLODE_BUG = 804,
SPELL_TWIN_TELEPORT_0 = 799,
// Vek'nilash
SPELL_UPPERCUT = 26007,
SPELL_UNBALANCING_STRIKE = 26613,
SPELL_SHADOWBOLT = 26006,
SPELL_BLIZZARD = 26607,
SPELL_ARCANEBURST = 568,
SPELL_BERSERK = 27680,
SPELL_MUTATE_BUG = 802,
// Bugs
SPELL_VIRULENT_POISON_PROC = 22413
};
enum Sound
enum Actions
{
SOUND_VL_AGGRO = 8657, //8657 - Aggro - To Late
SOUND_VL_KILL = 8658, //8658 - Kill - You will not
SOUND_VL_DEATH = 8659, //8659 - Death
SOUND_VN_DEATH = 8660, //8660 - Death - Feel
SOUND_VN_AGGRO = 8661, //8661 - Aggro - Let none
SOUND_VN_KILL = 8662, //8661 - Kill - your fate
ACTION_START_INTRO = 0,
ACTION_CANCEL_INTRO = 1,
ACTION_AFTER_TELEPORT = 2
};
enum Say
{
SAY_INTRO_0 = 0,
SAY_INTRO_1 = 1,
SAY_INTRO_2 = 2,
SAY_KILL = 3,
SAY_DEATH = 4,
EMOTE_ENRAGE = 5,
EMOTE_MASTERS_EYE_AT = 0,
};
enum Sounds
{
SOUND_VK_AGGRO = 8657,
SOUND_VN_AGGRO = 8661
};
enum Misc
{
PULL_RANGE = 50,
ABUSE_BUG_RANGE = 20,
VEKLOR_DIST = 20, // VL will not come to melee when attacking
TELEPORTTIME = 30000
GROUP_INTRO = 0,
NPC_QIRAJI_SCARAB = 15316,
NPC_QIRAJI_SCORPION = 15317,
FACTION_HOSTILE = 16
};
constexpr float veklorOrientationIntro = 2.241519f;
constexpr float veknilashOrientationIntro = 1.144451f;
struct boss_twinemperorsAI : public BossAI
{
boss_twinemperorsAI(Creature* creature): BossAI(creature, DATA_TWIN_EMPERORS) { }
uint32 Heal_Timer;
uint32 Teleport_Timer;
bool AfterTeleport;
uint32 AfterTeleportTimer;
bool DontYellWhenDead;
uint32 Abuse_Bug_Timer, BugsTimer;
bool tspellcast;
uint32 EnrageTimer;
virtual bool IAmVeklor() = 0;
void Reset() override = 0;
virtual void CastSpellOnBug(Creature* target) = 0;
void TwinReset()
boss_twinemperorsAI(Creature* creature): BossAI(creature, DATA_TWIN_EMPERORS), _introDone(false)
{
Heal_Timer = 0; // first heal immediately when they get close together
Teleport_Timer = TELEPORTTIME;
AfterTeleport = false;
tspellcast = false;
AfterTeleportTimer = 0;
Abuse_Bug_Timer = urand(10000, 17000);
BugsTimer = 2000;
me->ClearUnitState(UNIT_STATE_STUNNED);
DontYellWhenDead = false;
EnrageTimer = 15 * 60000;
me->SetStandState(UNIT_STAND_STATE_KNEEL);
instance->HandleGameObject(instance->GetGuidData(AQ40_DOOR_1), true);
}
Creature* GetOtherBoss()
{
return ObjectAccessor::GetCreature(*me, instance->GetGuidData(IAmVeklor() ? DATA_VEKNILASH : DATA_VEKLOR));
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
Unit* pOtherBoss = GetOtherBoss();
if (pOtherBoss)
{
float dPercent = ((float)damage) / ((float)me->GetMaxHealth());
int odmg = (int)(dPercent * ((float)pOtherBoss->GetMaxHealth()));
int ohealth = pOtherBoss->GetHealth() - odmg;
pOtherBoss->SetHealth(ohealth > 0 ? ohealth : 0);
if (ohealth <= 0)
_scheduler.SetValidator([this]
{
pOtherBoss->setDeathState(JUST_DIED);
pOtherBoss->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
Creature* GetTwin()
{
return instance->GetCreature(IAmVeklor() ? DATA_VEKNILASH : DATA_VEKLOR);
}
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (attacker)
{
if (attacker->GetEntry() == NPC_VEKLOR || attacker->GetEntry() == NPC_VEKNILASH)
{
me->LowerPlayerDamageReq(damage);
return;
}
if (Creature* twin = GetTwin())
{
float dmgPct = damage / (float)me->GetMaxHealth();
int32 actualDmg = dmgPct * twin->GetMaxHealth();
twin->CastCustomSpell(twin, SPELL_TWIN_EMPATHY, &actualDmg, nullptr, nullptr, true);
}
}
}
void JustDied(Unit* /*killer*/) override
void KilledUnit(Unit* victim) override
{
Creature* pOtherBoss = GetOtherBoss();
if (pOtherBoss)
{
pOtherBoss->SetHealth(0);
pOtherBoss->setDeathState(JUST_DIED);
pOtherBoss->SetDynamicFlag(UNIT_DYNFLAG_LOOTABLE);
CAST_AI(boss_twinemperorsAI, pOtherBoss->AI())->DontYellWhenDead = true;
}
if (!DontYellWhenDead) // I hope AI is not threaded
DoPlaySoundToSet(me, IAmVeklor() ? SOUND_VL_DEATH : SOUND_VN_DEATH);
instance->HandleGameObject(instance->GetGuidData(AQ40_DOOR_1), true);
instance->HandleGameObject(instance->GetGuidData(AQ40_DOOR_2), true);
if (victim && victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_KILL);
}
void KilledUnit(Unit* /*victim*/) override
void EnterEvadeMode(EvadeReason why) override
{
DoPlaySoundToSet(me, IAmVeklor() ? SOUND_VL_KILL : SOUND_VN_KILL);
BossAI::EnterEvadeMode(why);
if (Creature* twin = GetTwin())
if (!twin->IsInEvadeMode())
twin->AI()->EnterEvadeMode(why);
_scheduler.CancelAll();
}
void JustDied(Unit* killer) override
{
if (Creature* twin = GetTwin())
if (twin->IsAlive())
Unit::Kill(me, twin);
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
void DoAction(int32 action) override
{
if (action == ACTION_CANCEL_INTRO)
{
_introDone = true;
_scheduler.CancelGroup(GROUP_INTRO);
return;
}
if (action == ACTION_AFTER_TELEPORT)
{
DoResetThreat();
me->SetReactState(REACT_PASSIVE);
DoCastSelf(SPELL_TWIN_TELEPORT_VISUAL, true);
_scheduler.DelayAll(2300ms);
_scheduler.Schedule(2s, [this](TaskContext /*context*/)
{
me->SetReactState(REACT_AGGRESSIVE);
me->SetControlled(false, UNIT_STATE_ROOT);
if (Unit* victim = me->SelectNearestTarget())
{
me->AddThreat(victim, 200000.f);
AttackStart(victim);
}
});
}
if (action != ACTION_START_INTRO)
return;
_scheduler.Schedule(5s, [this](TaskContext /*context*/)
{
me->SetStandState(UNIT_STAND_STATE_STAND);
me->LoadEquipment(1, true);
});
if (IAmVeklor())
{
_scheduler
.Schedule(12s, GROUP_INTRO, [this](TaskContext /*context*/)
{
Talk(SAY_INTRO_0);
})
.Schedule(20s, GROUP_INTRO, [this](TaskContext /*context*/)
{
Talk(SAY_INTRO_1);
})
.Schedule(28s, GROUP_INTRO, [this](TaskContext /*context*/)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
})
.Schedule(30s, GROUP_INTRO, [this](TaskContext /*context*/)
{
me->SetFacingTo(veklorOrientationIntro);
Talk(SAY_INTRO_2);
})
.Schedule(33s, GROUP_INTRO, [this](TaskContext /*context*/)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
_introDone = true;
});
}
else
{
_scheduler
.Schedule(17s, GROUP_INTRO, [this](TaskContext /*context*/)
{
Talk(SAY_INTRO_0);
})
.Schedule(23s, GROUP_INTRO, [this](TaskContext /*context*/)
{
Talk(SAY_INTRO_1);
})
.Schedule(28s, GROUP_INTRO, [this](TaskContext /*context*/)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
})
.Schedule(32s, GROUP_INTRO, [this](TaskContext /*context*/)
{
me->SetFacingTo(veknilashOrientationIntro);
Talk(SAY_INTRO_2);
})
.Schedule(33s, GROUP_INTRO, [this](TaskContext /*context*/)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
_introDone = true;
});
}
}
void EnterCombat(Unit* who) override
{
DoZoneInCombat();
Creature* pOtherBoss = GetOtherBoss();
if (pOtherBoss)
BossAI::EnterCombat(who);
if (!_introDone)
{
/// @todo we should activate the other boss location so he can start attackning even if nobody
// is near I dont know how to do that
ScriptedAI* otherAI = CAST_AI(ScriptedAI, pOtherBoss->AI());
if (!pOtherBoss->IsInCombat())
DoAction(ACTION_CANCEL_INTRO);
if (Creature* twin = GetTwin())
twin->AI()->DoAction(ACTION_CANCEL_INTRO);
}
if (Creature* twin = GetTwin())
if (!twin->IsInCombat())
twin->AI()->AttackStart(who);
_scheduler
.Schedule(15min, [this](TaskContext /*context*/)
{
DoPlaySoundToSet(me, IAmVeklor() ? SOUND_VL_AGGRO : SOUND_VN_AGGRO);
otherAI->AttackStart(who);
otherAI->DoZoneInCombat();
}
}
instance->HandleGameObject(instance->GetGuidData(AQ40_DOOR_1), false);
}
void SpellHit(Unit* caster, SpellInfo const* entry) override
{
if (caster == me)
return;
Creature* pOtherBoss = GetOtherBoss();
if (entry->Id != SPELL_HEAL_BROTHER || !pOtherBoss)
return;
// add health so we keep same percentage for both brothers
uint32 mytotal = me->GetMaxHealth(), histotal = pOtherBoss->GetMaxHealth();
float mult = ((float)mytotal) / ((float)histotal);
if (mult < 1)
mult = 1.0f / mult;
#define HEAL_BROTHER_AMOUNT 30000.0f
uint32 largerAmount = (uint32)((HEAL_BROTHER_AMOUNT * mult) - HEAL_BROTHER_AMOUNT);
if (mytotal > histotal)
{
uint32 h = me->GetHealth() + largerAmount;
me->SetHealth(std::min(mytotal, h));
}
else
{
uint32 h = pOtherBoss->GetHealth() + largerAmount;
pOtherBoss->SetHealth(std::min(histotal, h));
}
}
void TryHealBrother(uint32 diff)
{
if (IAmVeklor()) // this spell heals caster and the other brother so let VN cast it
return;
if (Heal_Timer <= diff)
{
Unit* pOtherBoss = GetOtherBoss();
if (pOtherBoss && pOtherBoss->IsWithinDist(me, 60))
{
DoCast(pOtherBoss, SPELL_HEAL_BROTHER);
Heal_Timer = 1000;
}
}
else Heal_Timer -= diff;
}
void TeleportToMyBrother()
{
Teleport_Timer = TELEPORTTIME;
if (IAmVeklor())
return; // mechanics handled by veknilash so they teleport exactly at the same time and to correct coordinates
Creature* pOtherBoss = GetOtherBoss();
if (pOtherBoss)
{
//me->Yell("Teleporting ...", LANG_UNIVERSAL);
Position thisPos;
thisPos.Relocate(me);
Position otherPos;
otherPos.Relocate(pOtherBoss);
pOtherBoss->SetPosition(thisPos);
me->SetPosition(otherPos);
SetAfterTeleport();
CAST_AI(boss_twinemperorsAI, pOtherBoss->AI())->SetAfterTeleport();
}
}
void SetAfterTeleport()
{
me->InterruptNonMeleeSpells(false);
DoStopAttack();
DoResetThreat();
DoCast(me, SPELL_TWIN_TELEPORT_VISUAL);
me->AddUnitState(UNIT_STATE_STUNNED);
AfterTeleport = true;
AfterTeleportTimer = 2000;
tspellcast = false;
}
bool TryActivateAfterTTelep(uint32 diff)
{
if (AfterTeleport)
{
if (!tspellcast)
{
me->ClearUnitState(UNIT_STATE_STUNNED);
DoCast(me, SPELL_TWIN_TELEPORT);
me->AddUnitState(UNIT_STATE_STUNNED);
}
tspellcast = true;
if (AfterTeleportTimer <= diff)
{
AfterTeleport = false;
me->ClearUnitState(UNIT_STATE_STUNNED);
if (Unit* nearu = me->SelectNearestTarget(100))
if (IAmVeklor())
{
//DoYell(nearu->GetName(), LANG_UNIVERSAL);
AttackStart(nearu);
me->AddThreat(nearu, 10000);
}
return true;
}
else
{
AfterTeleportTimer -= diff;
// update important timers which would otherwise get skipped
if (EnrageTimer > diff)
EnrageTimer -= diff;
else
EnrageTimer = 0;
if (Teleport_Timer > diff)
Teleport_Timer -= diff;
else
Teleport_Timer = 0;
return false;
}
}
else
{
return true;
}
}
void MoveInLineOfSight(Unit* who) override
{
if (!who || me->GetVictim())
return;
if (me->CanCreatureAttack(who))
{
if (me->IsWithinDistInMap(who, PULL_RANGE, true, false) && me->GetDistanceZ(who) <= /*CREATURE_Z_ATTACK_RANGE*/7 /*there are stairs*/)
{
//if (who->HasStealthAura())
// who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
AttackStart(who);
}
}
}
Creature* RespawnNearbyBugsAndGetOne()
{
std::list<Creature*> lUnitList;
me->GetCreatureListWithEntryInGrid(lUnitList, 15316, 150.0f);
me->GetCreatureListWithEntryInGrid(lUnitList, 15317, 150.0f);
if (lUnitList.empty())
return nullptr;
Creature* nearb = nullptr;
for (std::list<Creature*>::const_iterator iter = lUnitList.begin(); iter != lUnitList.end(); ++iter)
{
Creature* c = *iter;
if (c)
{
if (c->isDead())
{
c->Respawn();
c->SetFaction(FACTION_CREATURE);
c->RemoveAllAuras();
}
if (c->IsWithinDistInMap(me, ABUSE_BUG_RANGE))
{
if (!nearb || (rand() % 4) == 0)
nearb = c;
}
}
}
return nearb;
}
void HandleBugs(uint32 diff)
{
if (BugsTimer < diff || Abuse_Bug_Timer <= diff)
{
Creature* c = RespawnNearbyBugsAndGetOne();
if (Abuse_Bug_Timer <= diff)
{
if (c)
{
CastSpellOnBug(c);
Abuse_Bug_Timer = urand(10000, 17000);
DoCastSelf(SPELL_FRENZY, true);
Talk(EMOTE_ENRAGE);
}
else
{
Abuse_Bug_Timer = 1000;
}
}
else
DoCastSelf(SPELL_BERSERK, true);
})
.Schedule(3600ms, [this](TaskContext context) // according to sniffs it should be casted by both emperors.
{
Abuse_Bug_Timer -= diff;
}
BugsTimer = 2000;
}
else
{
BugsTimer -= diff;
Abuse_Bug_Timer -= diff;
}
if (Creature* twin = GetTwin())
{
if (me->IsWithinDist(twin, 60.f))
DoCast(twin, SPELL_HEAL_BROTHER, true);
}
context.Repeat();
});
}
void CheckEnrage(uint32 diff)
void UpdateAI(uint32 diff) override
{
if (EnrageTimer <= diff)
{
if (!me->IsNonMeleeSpellCast(true))
if (!UpdateVictim() && _introDone)
return;
_scheduler.Update(diff, [this]
{
DoCast(me, SPELL_BERSERK);
EnrageTimer = 60 * 60000;
}
else EnrageTimer = 0;
}
else EnrageTimer -= diff;
if (!IAmVeklor())
DoMeleeAttackIfReady();
});
}
virtual bool IAmVeklor() = 0;
protected:
TaskScheduler _scheduler;
bool _introDone;
};
struct boss_veknilash : public boss_twinemperorsAI
@@ -385,192 +300,172 @@ struct boss_veknilash : public boss_twinemperorsAI
bool IAmVeklor() override { return false; }
uint32 UpperCut_Timer;
uint32 UnbalancingStrike_Timer;
uint32 Scarabs_Timer;
int Rand;
int RandX;
int RandY;
Creature* Summoned;
void Reset() override
void EnterCombat(Unit* who) override
{
TwinReset();
UpperCut_Timer = urand(14000, 29000);
UnbalancingStrike_Timer = urand(8000, 18000);
Scarabs_Timer = urand(7000, 14000);
boss_twinemperorsAI::EnterCombat(who);
//Added. Can be removed if its included in DB.
me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true);
}
DoPlaySoundToSet(me, SOUND_VN_AGGRO);
void CastSpellOnBug(Creature* target) override
{
target->SetFaction(FACTION_MONSTER);
target->AI()->AttackStart(me->GetThreatMgr().getHostileTarget());
target->AddAura(SPELL_MUTATE_BUG, target);
target->SetFullHealth();
}
void UpdateAI(uint32 diff) override
{
//Return since we have no target
if (!UpdateVictim())
return;
if (!TryActivateAfterTTelep(diff))
return;
//UnbalancingStrike_Timer
if (UnbalancingStrike_Timer <= diff)
{
DoCastVictim(SPELL_UNBALANCING_STRIKE);
UnbalancingStrike_Timer = 8000 + rand() % 12000;
}
else UnbalancingStrike_Timer -= diff;
if (UpperCut_Timer <= diff)
{
Unit* randomMelee = SelectTarget(SelectTargetMethod::Random, 0, NOMINAL_MELEE_RANGE, true);
if (randomMelee)
DoCast(randomMelee, SPELL_UPPERCUT);
UpperCut_Timer = 15000 + rand() % 15000;
}
else UpperCut_Timer -= diff;
HandleBugs(diff);
//Heal brother when 60yrds close
TryHealBrother(diff);
//Teleporting to brother
if (Teleport_Timer <= diff)
{
TeleportToMyBrother();
}
else Teleport_Timer -= diff;
CheckEnrage(diff);
DoMeleeAttackIfReady();
_scheduler
.Schedule(14s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_UPPERCUT, 0, me->GetMeleeReach(), true);
context.Repeat(4s, 15s);
})
.Schedule(12s, [this](TaskContext context)
{
DoCastVictim(SPELL_UNBALANCING_STRIKE);
context.Repeat(8s, 20s);
})
.Schedule(16s, [this](TaskContext context)
{
DoCastAOE(SPELL_MUTATE_BUG);
context.Repeat(10s, 20s);
});
}
};
struct boss_veklor : public boss_twinemperorsAI
{
boss_veklor(Creature* creature) : boss_twinemperorsAI(creature) { }
boss_veklor(Creature* creature) : boss_twinemperorsAI(creature)
{
me->SetFloatValue(UNIT_FIELD_COMBATREACH, 45.f);
}
bool IAmVeklor() override { return true; }
uint32 ShadowBolt_Timer;
uint32 Blizzard_Timer;
uint32 ArcaneBurst_Timer;
uint32 Scorpions_Timer;
int Rand;
int RandX;
Creature* Summoned;
void Reset() override
void EnterCombat(Unit* who) override
{
TwinReset();
ShadowBolt_Timer = 0;
Blizzard_Timer = urand(15000, 20000);
ArcaneBurst_Timer = 1000;
Scorpions_Timer = urand(7000, 14000);
boss_twinemperorsAI::EnterCombat(who);
//Added. Can be removed if its included in DB.
me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true);
DoPlaySoundToSet(me, SOUND_VK_AGGRO);
_scheduler
.Schedule(4s, [this](TaskContext context)
{
DoCastVictim(SPELL_SHADOW_BOLT);
context.Repeat(2500ms);
})
.Schedule(10s, 15s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_BLIZZARD, 0, 45.f);
context.Repeat(5s, 12s);
})
.Schedule(1s, [this](TaskContext context)
{
if (me->SelectNearestPlayer(NOMINAL_MELEE_RANGE))
DoCastAOE(SPELL_ARCANE_BURST);
context.Repeat(7s, 12s);
})
.Schedule(30s, 40s, [this](TaskContext context)
{
DoCastSelf(SPELL_TWIN_TELEPORT_0);
context.Repeat();
})
.Schedule(5s, [this](TaskContext context)
{
DoCastAOE(SPELL_EXPLODE_BUG);
context.Repeat(4500ms, 10s);
});
}
void CastSpellOnBug(Creature* target) override
void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
{
target->SetFaction(FACTION_MONSTER);
target->AddAura(SPELL_EXPLODEBUG, target);
target->SetFullHealth();
if (spellInfo->Id == SPELL_TWIN_TELEPORT_0)
{
if (Creature* veknilash = GetTwin())
{
DoCastSelf(SPELL_TWIN_TELEPORT_1, true);
me->SetControlled(true, UNIT_STATE_ROOT);
Position veklorPos = me->GetPosition();
Position veknilashPos = veknilash->GetPosition();
me->NearTeleportTo(veknilashPos);
veknilash->CastSpell(veknilash, SPELL_TWIN_TELEPORT_1, true);
veknilash->SetControlled(true, UNIT_STATE_ROOT);
veknilash->NearTeleportTo(veklorPos);
veknilash->AI()->DoAction(ACTION_AFTER_TELEPORT);
DoAction(ACTION_AFTER_TELEPORT);
}
}
}
};
class at_twin_emperors : public OnlyOnceAreaTriggerScript
{
public:
at_twin_emperors() : OnlyOnceAreaTriggerScript("at_twin_emperors") { }
bool _OnTrigger(Player* player, const AreaTrigger* /*at*/) override
{
if (InstanceScript* instance = player->GetInstanceScript())
{
if (instance->GetBossState(DATA_TWIN_EMPERORS) != DONE)
{
if (Creature* mastersEye = instance->GetCreature(DATA_MASTERS_EYE))
{
mastersEye->AI()->Talk(EMOTE_MASTERS_EYE_AT, player);
mastersEye->DespawnOrUnsummon(11000);
mastersEye->m_Events.AddEventAtOffset([mastersEye, player]()
{
mastersEye->SetFacingToObject(player);
}, 3s);
}
if (Creature* veklor = instance->GetCreature(DATA_VEKLOR))
veklor->AI()->DoAction(ACTION_START_INTRO);
if (Creature* veknilash = instance->GetCreature(DATA_VEKNILASH))
veknilash->AI()->DoAction(ACTION_START_INTRO);
}
}
return false;
}
};
class spell_mutate_explode_bug : public SpellScript
{
PrepareSpellScript(spell_mutate_explode_bug);
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if([&](WorldObject const* target) -> bool
{
if (target->GetEntry() != NPC_QIRAJI_SCARAB && target->GetEntry() != NPC_QIRAJI_SCORPION)
return true;
if (Creature const* creature = target->ToCreature())
if (creature->HasAura(SPELL_EXPLODE_BUG) || creature->HasAura(SPELL_MUTATE_BUG))
return true;
return false;
});
Acore::Containers::RandomResize(targets, 1);
}
void UpdateAI(uint32 diff) override
void HandleOnHit()
{
//Return since we have no target
if (!UpdateVictim())
if (!GetHitUnit())
return;
// reset arcane burst after teleport - we need to do this because
// when VL jumps to VN's location there will be a warrior who will get only 2s to run away
// which is almost impossible
if (AfterTeleport)
ArcaneBurst_Timer = 5000;
if (!TryActivateAfterTTelep(diff))
Creature* target = GetHitUnit()->ToCreature();
if (!target)
return;
//ShadowBolt_Timer
if (ShadowBolt_Timer <= diff)
{
if (!me->IsWithinDist(me->GetVictim(), 45.0f))
me->GetMotionMaster()->MoveChase(me->GetVictim(), VEKLOR_DIST, 0);
else
DoCastVictim(SPELL_SHADOWBOLT);
ShadowBolt_Timer = 2000;
}
else ShadowBolt_Timer -= diff;
//Blizzard_Timer
if (Blizzard_Timer <= diff)
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45, true);
if (target)
{
DoCast(target, SPELL_BLIZZARD);
}
Blizzard_Timer = 15000 + rand() % 15000;
}
else Blizzard_Timer -= diff;
if (ArcaneBurst_Timer <= diff)
{
Unit* mvic;
if ((mvic = SelectTarget(SelectTargetMethod::MaxDistance, 0, NOMINAL_MELEE_RANGE, true)) != nullptr)
{
DoCast(mvic, SPELL_ARCANEBURST);
ArcaneBurst_Timer = 5000;
}
}
else ArcaneBurst_Timer -= diff;
HandleBugs(diff);
//Heal brother when 60yrds close
TryHealBrother(diff);
//Teleporting to brother
if (Teleport_Timer <= diff)
{
TeleportToMyBrother();
}
else Teleport_Timer -= diff;
CheckEnrage(diff);
//VL doesn't melee
//DoMeleeAttackIfReady();
if (m_scriptSpellId == SPELL_MUTATE_BUG)
target->CastSpell(target, SPELL_VIRULENT_POISON_PROC, true);
target->SetFaction(FACTION_HOSTILE);
target->SetReactState(REACT_AGGRESSIVE);
target->SetInCombatWithZone();
}
void AttackStart(Unit* who) override
void Register() override
{
if (!who)
return;
if (who->isTargetableForAttack())
{
// VL doesn't melee
if (me->Attack(who, false))
{
me->GetMotionMaster()->MoveChase(who, VEKLOR_DIST, 0);
me->AddThreat(who, 0.0f);
}
}
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mutate_explode_bug::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENTRY);
OnHit += SpellHitFn(spell_mutate_explode_bug::HandleOnHit);
}
};
@@ -578,4 +473,6 @@ void AddSC_boss_twinemperors()
{
RegisterTempleOfAhnQirajCreatureAI(boss_veknilash);
RegisterTempleOfAhnQirajCreatureAI(boss_veklor);
new at_twin_emperors();
RegisterSpellScript(spell_mutate_explode_bug);
}

View File

@@ -24,7 +24,10 @@ ObjectData const creatureData[] =
{
{ NPC_SARTURA, DATA_SARTURA },
{ NPC_EYE_OF_CTHUN, DATA_EYE_OF_CTHUN },
{ NPC_OURO_SPAWNER, DATA_OURO_SPAWNER }
{ NPC_OURO_SPAWNER, DATA_OURO_SPAWNER },
{ NPC_MASTERS_EYE, DATA_MASTERS_EYE },
{ NPC_VEKLOR, DATA_VEKLOR },
{ NPC_VEKNILASH, DATA_VEKNILASH }
};
class instance_temple_of_ahnqiraj : public InstanceMapScript
@@ -46,15 +49,10 @@ public:
SetBossNumber(MAX_BOSS_NUMBER);
}
//If Vem is dead...
bool IsBossDied[3];
ObjectGuid SkeramGUID;
ObjectGuid VemGUID;
ObjectGuid KriGUID;
ObjectGuid YaujGUID;
ObjectGuid VeklorGUID;
ObjectGuid VeknilashGUID;
ObjectGuid ViscidusGUID;
ObjectGuid CThunGUID;
GuidVector CThunGraspGUIDs;
@@ -65,9 +63,6 @@ public:
void Initialize() override
{
IsBossDied[0] = false;
IsBossDied[1] = false;
IsBossDied[2] = false;
BugTrioDeathCount = 0;
CthunPhase = 0;
}
@@ -92,16 +87,6 @@ public:
case NPC_YAUJ:
YaujGUID = creature->GetGUID();
break;
case NPC_VEKLOR:
VeklorGUID = creature->GetGUID();
if (!creature->IsAlive())
{
HandleGameObject(doorGUIDs[1], true);
}
break;
case NPC_VEKNILASH:
VeknilashGUID = creature->GetGUID();
break;
case NPC_VISCIDUS:
ViscidusGUID = creature->GetGUID();
break;
@@ -109,6 +94,10 @@ public:
if (GetBossState(DATA_OURO) != DONE)
creature->Respawn();
break;
case NPC_MASTERS_EYE:
if (GetBossState(DATA_TWIN_EMPERORS) != DONE)
creature->Respawn(true);
break;
case NPC_CTHUN:
CThunGUID = creature->GetGUID();
if (!creature->IsAlive())
@@ -138,7 +127,7 @@ public:
break;
case AQ40_DOOR_2:
doorGUIDs[1] = go->GetGUID();
if (Creature* veklor = instance->GetCreature(VeklorGUID))
if (Creature* veklor = GetCreature(DATA_VEKLOR))
{
if (!veklor->IsAlive())
{
@@ -177,16 +166,6 @@ public:
{
switch (type)
{
case DATA_VEKLORISDEAD:
if (IsBossDied[1])
return 1;
break;
case DATA_VEKNILASHISDEAD:
if (IsBossDied[2])
return 1;
break;
case DATA_BUG_TRIO_DEATH:
return BugTrioDeathCount;
@@ -208,10 +187,6 @@ public:
return KriGUID;
case DATA_YAUJ:
return YaujGUID;
case DATA_VEKLOR:
return VeklorGUID;
case DATA_VEKNILASH:
return VeknilashGUID;
case DATA_VISCIDUS:
return ViscidusGUID;
case AQ40_DOOR_1:
@@ -234,12 +209,6 @@ public:
else
BugTrioDeathCount = 0;
break;
case DATA_VEKLOR_DEATH:
IsBossDied[1] = true;
break;
case DATA_VEKNILASH_DEATH:
IsBossDied[2] = true;
break;
case DATA_CTHUN_PHASE:
CthunPhase = data;
if (data == PHASE_CTHUN_DONE)
@@ -253,6 +222,8 @@ public:
}
}
break;
default:
break;
}
}

View File

@@ -42,17 +42,15 @@ enum DataTypes
DATA_BUG_TRIO_DEATH = 13,
DATA_OURO_SPAWNER = 14,
DATA_VEKLOR = 15,
DATA_VEKLORISDEAD = 16,
DATA_VEKLOR_DEATH = 17,
DATA_VEKNILASH = 18,
DATA_VEKNILASHISDEAD = 19,
DATA_VEKNILASH_DEATH = 20,
DATA_CTHUN_PHASE = 21,
DATA_EYE_OF_CTHUN = 22
DATA_VEKNILASH = 16,
DATA_CTHUN_PHASE = 17,
DATA_EYE_OF_CTHUN = 18,
DATA_MASTERS_EYE = 19
};
enum Creatures
{
NPC_MASTERS_EYE = 15963,
NPC_CTHUN = 15727,
NPC_EYE_OF_CTHUN = 15589,
NPC_CTHUN_PORTAL = 15896,