Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
ZhengPeiRu21
2022-09-07 09:35:58 -06:00
committed by GitHub
33 changed files with 2293 additions and 546 deletions

View File

@@ -55,6 +55,12 @@ struct boss_moam : public BossAI
{
boss_moam(Creature* creature) : BossAI(creature, DATA_MOAM) {}
void InitializeAI() override
{
me->m_CombatDistance = 50.0f;
Reset();
}
void Reset() override
{
_Reset();

View File

@@ -63,6 +63,13 @@ struct boss_sartura : public BossAI
{
boss_sartura(Creature* creature) : BossAI(creature, DATA_SARTURA) {}
void InitializeAI() override
{
me->m_CombatDistance = 60.f;
me->m_SightDistance = 60.f;
Reset();
}
void Reset() override
{
_Reset();

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,

View File

@@ -3676,6 +3676,32 @@ class spell_item_mirrens_drinking_hat : public SpellScript
}
};
class spell_item_snowman : public SpellScript
{
PrepareSpellScript(spell_item_snowman);
SpellCastResult CheckCast()
{
if (Player* caster = GetCaster()->ToPlayer())
{
if (Battleground* bg = caster->GetBattleground())
{
if (bg->GetStatus() == STATUS_WAIT_JOIN)
{
return SPELL_FAILED_NOT_READY;
}
}
}
return SPELL_CAST_OK;
}
void Register() override
{
OnCheckCast += SpellCheckCastFn(spell_item_snowman::CheckCast);
}
};
void AddSC_item_spell_scripts()
{
RegisterSpellScript(spell_item_massive_seaforium_charge);
@@ -3789,4 +3815,5 @@ void AddSC_item_spell_scripts()
RegisterSpellScript(spell_item_recall);
RegisterSpellScript(spell_item_wraith_scythe_drain_life);
RegisterSpellScript(spell_item_mirrens_drinking_hat);
RegisterSpellScript(spell_item_snowman);
}