Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2023-07-30 14:37:00 +08:00
43 changed files with 831 additions and 648 deletions

View File

@@ -121,8 +121,6 @@ struct boss_moroes : public BossAI
context.Repeat(5s);
}).Schedule(1min, 2min, GROUP_PRECOMBAT_TALK, [this](TaskContext context)
{
//this was not scheduled in the previous commit
//does this have to be removed?
Talk(SAY_OUT_OF_COMBAT);
context.Repeat(1min, 2min);
});

View File

@@ -15,13 +15,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
SDName: Boss_Netherspite
SD%Complete: 90
SDComment: Not sure about timing and portals placing
SDCategory: Karazhan
EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
@@ -29,19 +22,35 @@ EndScriptData */
#include "SpellAuraEffects.h"
#include "SpellScript.h"
enum Netherspite
enum Emotes
{
EMOTE_PHASE_PORTAL = 0,
EMOTE_PHASE_BANISH = 1,
EMOTE_PHASE_PORTAL = 0,
EMOTE_PHASE_BANISH = 1
};
SPELL_NETHERBURN_AURA = 30522,
SPELL_VOIDZONE = 37063,
SPELL_NETHER_INFUSION = 38688,
SPELL_NETHERBREATH = 38523,
SPELL_BANISH_VISUAL = 39833,
SPELL_BANISH_ROOT = 42716,
SPELL_EMPOWERMENT = 38549,
SPELL_NETHERSPITE_ROAR = 38684,
enum Spells
{
SPELL_NETHERBURN_AURA = 30522,
SPELL_VOIDZONE = 37063,
SPELL_NETHER_INFUSION = 38688,
SPELL_NETHERBREATH = 38523,
SPELL_BANISH_VISUAL = 39833,
SPELL_BANISH_ROOT = 42716,
SPELL_EMPOWERMENT = 38549,
SPELL_NETHERSPITE_ROAR = 38684
};
enum Portals
{
RED_PORTAL = 0, // Perseverence
GREEN_PORTAL = 1, // Serenity
BLUE_PORTAL = 2 // Dominance
};
enum Groups
{
PORTAL_PHASE = 0,
VANISH_PHASE = 1
};
const float PortalCoord[3][3] =
@@ -51,302 +60,255 @@ const float PortalCoord[3][3] =
{-11094.493164f, -1591.969238f, 279.949188f} // Back side
};
enum Netherspite_Portal
{
RED_PORTAL = 0, // Perseverence
GREEN_PORTAL = 1, // Serenity
BLUE_PORTAL = 2 // Dominance
};
const uint32 PortalID[3] = {17369, 17367, 17368};
const uint32 PortalID[3] = {17369, 17367, 17368};
const uint32 PortalVisual[3] = {30487, 30490, 30491};
const uint32 PortalBeam[3] = {30465, 30464, 30463};
const uint32 PlayerBuff[3] = {30421, 30422, 30423};
const uint32 NetherBuff[3] = {30466, 30467, 30468};
const uint32 PortalBeam[3] = {30465, 30464, 30463};
const uint32 PlayerBuff[3] = {30421, 30422, 30423};
const uint32 NetherBuff[3] = {30466, 30467, 30468};
const uint32 PlayerDebuff[3] = {38637, 38638, 38639};
class boss_netherspite : public CreatureScript
struct boss_netherspite : public BossAI
{
public:
boss_netherspite() : CreatureScript("boss_netherspite") { }
boss_netherspite(Creature* creature) : BossAI(creature, DATA_NETHERSPITE) {}
CreatureAI* GetAI(Creature* creature) const override
bool IsBetween(WorldObject* u1, WorldObject* target, WorldObject* u2) // the in-line checker
{
return GetKarazhanAI<boss_netherspiteAI>(creature);
if (!u1 || !u2 || !target)
return false;
float xn, yn, xp, yp, xh, yh;
xn = u1->GetPositionX();
yn = u1->GetPositionY();
xp = u2->GetPositionX();
yp = u2->GetPositionY();
xh = target->GetPositionX();
yh = target->GetPositionY();
// check if target is between (not checking distance from the beam yet)
if (dist(xn, yn, xh, yh) >= dist(xn, yn, xp, yp) || dist(xp, yp, xh, yh) >= dist(xn, yn, xp, yp))
return false;
// check distance from the beam
return (std::abs((xn - xp) * yh + (yp - yn) * xh - xn * yp + xp * yn) / dist(xn, yn, xp, yp) < 1.5f);
}
struct boss_netherspiteAI : public ScriptedAI
float dist(float xa, float ya, float xb, float yb) // auxiliary method for distance
{
boss_netherspiteAI(Creature* creature) : ScriptedAI(creature)
return std::sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb));
}
void Reset() override
{
BossAI::Reset();
berserk = false;
HandleDoors(true);
DestroyPortals();
}
void SummonPortals()
{
uint8 r = rand() % 4;
uint8 pos[3];
pos[RED_PORTAL] = ((r % 2) ? (r > 1 ? 2 : 1) : 0);
pos[GREEN_PORTAL] = ((r % 2) ? 0 : (r > 1 ? 2 : 1));
pos[BLUE_PORTAL] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0)
for (int i = 0; i < 3; ++i)
{
instance = creature->GetInstanceScript();
}
InstanceScript* instance;
bool PortalPhase;
bool Berserk;
uint32 PhaseTimer; // timer for phase switching
uint32 VoidZoneTimer;
uint32 NetherInfusionTimer; // berserking timer
uint32 NetherbreathTimer;
uint32 EmpowermentTimer;
uint32 PortalTimer; // timer for beam checking
ObjectGuid PortalGUID[3]; // guid's of portals
ObjectGuid BeamerGUID[3]; // guid's of auxiliary beaming portals
ObjectGuid BeamTarget[3]; // guid's of portals' current targets
bool IsBetween(WorldObject* u1, WorldObject* target, WorldObject* u2) // the in-line checker
{
if (!u1 || !u2 || !target)
return false;
float xn, yn, xp, yp, xh, yh;
xn = u1->GetPositionX();
yn = u1->GetPositionY();
xp = u2->GetPositionX();
yp = u2->GetPositionY();
xh = target->GetPositionX();
yh = target->GetPositionY();
// check if target is between (not checking distance from the beam yet)
if (dist(xn, yn, xh, yh) >= dist(xn, yn, xp, yp) || dist(xp, yp, xh, yh) >= dist(xn, yn, xp, yp))
return false;
// check distance from the beam
return (std::abs((xn - xp) * yh + (yp - yn) * xh - xn * yp + xp * yn) / dist(xn, yn, xp, yp) < 1.5f);
}
float dist(float xa, float ya, float xb, float yb) // auxiliary method for distance
{
return std::sqrt((xa - xb) * (xa - xb) + (ya - yb) * (ya - yb));
}
void Reset() override
{
Berserk = false;
NetherInfusionTimer = 540000;
VoidZoneTimer = 15000;
NetherbreathTimer = 3000;
HandleDoors(true);
DestroyPortals();
}
void SummonPortals()
{
uint8 r = rand() % 4;
uint8 pos[3];
pos[RED_PORTAL] = ((r % 2) ? (r > 1 ? 2 : 1) : 0);
pos[GREEN_PORTAL] = ((r % 2) ? 0 : (r > 1 ? 2 : 1));
pos[BLUE_PORTAL] = (r > 1 ? 1 : 2); // Blue Portal not on the left side (0)
for (int i = 0; i < 3; ++i)
if (Creature* portal = me->SummonCreature(PortalID[i], PortalCoord[pos[i]][0], PortalCoord[pos[i]][1], PortalCoord[pos[i]][2], 0, TEMPSUMMON_TIMED_DESPAWN, 60000))
{
PortalGUID[i] = portal->GetGUID();
portal->AddAura(PortalVisual[i], portal);
}
}
void DestroyPortals()
{
for (int i = 0; i < 3; ++i)
if (Creature* portal = me->SummonCreature(PortalID[i], PortalCoord[pos[i]][0], PortalCoord[pos[i]][1], PortalCoord[pos[i]][2], 0, TEMPSUMMON_TIMED_DESPAWN, 60000))
{
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
portal->DisappearAndDie();
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
portal->DisappearAndDie();
PortalGUID[i].Clear();
BeamTarget[i].Clear();
PortalGUID[i] = portal->GetGUID();
portal->AddAura(PortalVisual[i], portal);
}
}
}
void UpdatePortals() // Here we handle the beams' behavior
void DestroyPortals()
{
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j) // j = color
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[j]))
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[i]))
{
portal->DisappearAndDie();
}
if (Creature* portal = ObjectAccessor::GetCreature(*me, BeamerGUID[i]))
{
portal->DisappearAndDie();
}
PortalGUID[i].Clear();
BeamTarget[i].Clear();
}
}
void UpdatePortals() // Here we handle the beams' behavior
{
for (int j = 0; j < 3; ++j) // j = color
{
if (Creature* portal = ObjectAccessor::GetCreature(*me, PortalGUID[j]))
{
// the one who's been cast upon before
Unit* current = ObjectAccessor::GetUnit(*portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit* target = me;
if (Map* map = me->GetMap())
{
// the one who's been cast upon before
Unit* current = ObjectAccessor::GetUnit(*portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit* target = me;
Map::PlayerList const& players = map->GetPlayers();
if (Map* map = me->GetMap())
// get the best suitable target
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
{
Map::PlayerList const& players = map->GetPlayers();
// get the best suitable target
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
{
Player* p = i->GetSource();
if (p && p->IsAlive() // alive
&& (!target || target->GetDistance2d(portal) > p->GetDistance2d(portal)) // closer than current best
&& !p->HasAura(PlayerDebuff[j]) // not exhausted
&& !p->HasAura(PlayerBuff[(j + 1) % 3]) // not on another beam
&& !p->HasAura(PlayerBuff[(j + 2) % 3])
&& IsBetween(me, p, portal)) // on the beam
target = p;
}
Player* p = i->GetSource();
if (p && p->IsAlive() // alive
&& (!target || target->GetDistance2d(portal) > p->GetDistance2d(portal)) // closer than current best
&& !p->HasAura(PlayerDebuff[j]) // not exhausted
&& !p->HasAura(PlayerBuff[(j + 1) % 3]) // not on another beam
&& !p->HasAura(PlayerBuff[(j + 2) % 3])
&& IsBetween(me, p, portal)) // on the beam
target = p;
}
// buff the target
if (target->GetTypeId() == TYPEID_PLAYER)
target->AddAura(PlayerBuff[j], target);
else
target->AddAura(NetherBuff[j], target);
// cast visual beam on the chosen target if switched
// simple target switching isn't working -> using BeamerGUID to cast (workaround)
if (!current || target != current)
{
BeamTarget[j] = target->GetGUID();
// remove currently beaming portal
if (Creature* beamer = ObjectAccessor::GetCreature(*portal, BeamerGUID[j]))
{
beamer->CastSpell(target, PortalBeam[j], false);
beamer->DisappearAndDie();
BeamerGUID[j].Clear();
}
// create new one and start beaming on the target
if (Creature* beamer = portal->SummonCreature(PortalID[j], portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), portal->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 60000))
{
beamer->CastSpell(target, PortalBeam[j], false);
BeamerGUID[j] = beamer->GetGUID();
}
}
// aggro target if Red Beam
if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER)
me->GetThreatMgr().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim()));
}
// buff the target
if (target->GetTypeId() == TYPEID_PLAYER)
{
target->AddAura(PlayerBuff[j], target);
}
else
{
target->AddAura(NetherBuff[j], target);
}
// cast visual beam on the chosen target if switched
// simple target switching isn't working -> using BeamerGUID to cast (workaround)
if (!current || target != current)
{
BeamTarget[j] = target->GetGUID();
// remove currently beaming portal
if (Creature* beamer = ObjectAccessor::GetCreature(*portal, BeamerGUID[j]))
{
beamer->CastSpell(target, PortalBeam[j], false);
beamer->DisappearAndDie();
BeamerGUID[j].Clear();
}
// create new one and start beaming on the target
if (Creature* beamer = portal->SummonCreature(PortalID[j], portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), portal->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN, 60000))
{
beamer->CastSpell(target, PortalBeam[j], false);
BeamerGUID[j] = beamer->GetGUID();
}
}
// aggro target if Red Beam
if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER)
{
me->GetThreatMgr().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim()));
}
}
}
}
void SwitchToPortalPhase()
void SwitchToPortalPhase()
{
scheduler.CancelGroup(VANISH_PHASE);
me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT);
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL);
SummonPortals();
scheduler.Schedule(60s, [this](TaskContext /*context*/)
{
me->RemoveAurasDueToSpell(SPELL_BANISH_ROOT);
me->RemoveAurasDueToSpell(SPELL_BANISH_VISUAL);
SummonPortals();
PhaseTimer = 60000;
PortalPhase = true;
PortalTimer = 10000;
EmpowermentTimer = 10000;
Talk(EMOTE_PHASE_PORTAL);
}
void SwitchToBanishPhase()
{
me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA);
DoCast(me, SPELL_BANISH_VISUAL, true);
DoCast(me, SPELL_BANISH_ROOT, true);
DestroyPortals();
PhaseTimer = 30000;
PortalPhase = false;
Talk(EMOTE_PHASE_BANISH);
for (uint8 i = 0; i < 3; ++i)
me->RemoveAurasDueToSpell(NetherBuff[i]);
}
void HandleDoors(bool open) // Massive Door switcher
{
if (GameObject* Door = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_GO_MASSIVE_DOOR) ))
Door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
}
void JustEngagedWith(Unit* /*who*/) override
{
HandleDoors(false);
SwitchToPortalPhase();
DoZoneInCombat();
}
void JustDied(Unit* /*killer*/) override
{
HandleDoors(true);
DestroyPortals();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToBanishPhase();
return;
// Void Zone
if (VoidZoneTimer <= diff)
{
DoCast(SelectTarget(SelectTargetMethod::Random, 1, 45, true), SPELL_VOIDZONE, true);
VoidZoneTimer = 15000;
}
else
VoidZoneTimer -= diff;
}).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
{
UpdatePortals();
context.Repeat(1s);
}).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
{
DoCastSelf(SPELL_EMPOWERMENT);
me->AddAura(SPELL_NETHERBURN_AURA, me);
context.Repeat(90s);
});
Talk(EMOTE_PHASE_PORTAL);
}
// NetherInfusion Berserk
if (!Berserk && NetherInfusionTimer <= diff)
void SwitchToBanishPhase()
{
scheduler.CancelGroup(PORTAL_PHASE);
me->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
me->RemoveAurasDueToSpell(SPELL_NETHERBURN_AURA);
DoCastSelf(SPELL_BANISH_VISUAL, true);
DoCastSelf(SPELL_BANISH_ROOT, true);
DestroyPortals();
scheduler.Schedule(30s, [this](TaskContext /*context*/)
{
if (!me->IsNonMeleeSpellCast(false))
{
me->AddAura(SPELL_NETHER_INFUSION, me);
DoCast(me, SPELL_NETHERSPITE_ROAR);
Berserk = true;
SwitchToPortalPhase();
return;
}
else
NetherInfusionTimer -= diff;
if (PortalPhase) // PORTAL PHASE
{
// Distribute beams and buffs
if (PortalTimer <= diff)
{
UpdatePortals();
PortalTimer = 1000;
}
else
PortalTimer -= diff;
// Empowerment & Nether Burn
if (EmpowermentTimer <= diff)
{
DoCast(me, SPELL_EMPOWERMENT);
me->AddAura(SPELL_NETHERBURN_AURA, me);
EmpowermentTimer = 90000;
}
else
EmpowermentTimer -= diff;
if (PhaseTimer <= diff)
{
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToBanishPhase();
return;
}
}
else
PhaseTimer -= diff;
}
else // BANISH PHASE
{
// Netherbreath
if (NetherbreathTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40, true))
DoCast(target, SPELL_NETHERBREATH);
NetherbreathTimer = urand(5000, 7000);
}
else
NetherbreathTimer -= diff;
if (PhaseTimer <= diff)
{
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToPortalPhase();
return;
}
}
else
PhaseTimer -= diff;
}
DoMeleeAttackIfReady();
}).Schedule(10s, VANISH_PHASE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_NETHERBREATH, 0, 40.0f, true);
context.Repeat(5s, 7s);
});
Talk(EMOTE_PHASE_BANISH);
for (uint8 i = 0; i < 3; ++i)
{
me->RemoveAurasDueToSpell(NetherBuff[i]);
}
};
}
void HandleDoors(bool open)
{
if (GameObject* door = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_GO_MASSIVE_DOOR)))
{
door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
HandleDoors(false);
SwitchToPortalPhase();
DoZoneInCombat();
scheduler.Schedule(15s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_VOIDZONE, 1, 45.0f, true, true);
context.Repeat(15s);
}).Schedule(9min, [this](TaskContext /*context*/)
{
if (!berserk)
{
DoCastSelf(SPELL_NETHER_INFUSION);
DoCastAOE(SPELL_NETHERSPITE_ROAR);
berserk = true;
}
});
}
void JustDied(Unit* killer) override
{
BossAI::JustDied(killer);
HandleDoors(true);
DestroyPortals();
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
private:
bool berserk;
ObjectGuid PortalGUID[3]; // guid's of portals
ObjectGuid BeamerGUID[3]; // guid's of auxiliary beaming portals
ObjectGuid BeamTarget[3]; // guid's of portals' current targets
};
class spell_nether_portal_perseverence : public AuraScript
@@ -366,6 +328,6 @@ class spell_nether_portal_perseverence : public AuraScript
void AddSC_boss_netherspite()
{
new boss_netherspite();
RegisterKarazhanCreatureAI(boss_netherspite);
RegisterSpellScript(spell_nether_portal_perseverence);
}

View File

@@ -25,6 +25,7 @@ EndScriptData */
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "TaskScheduler.h"
#include "karazhan.h"
enum Spells
@@ -52,6 +53,12 @@ enum Says
EMOTE_BREATH = 4
};
enum Groups
{
GROUP_GROUND = 0,
GROUP_FLYING = 1
};
float IntroWay[8][3] =
{
{-11053.37f, -1794.48f, 149.00f},
@@ -62,218 +69,270 @@ float IntroWay[8][3] =
{-11128.73f, -1929.75f, 125.00f},
{-11140.00f, -1915.00f, 122.00f},
{-11163.00f, -1903.00f, 91.473f}
};
}; //TODO: move to table
class boss_nightbane : public CreatureScript
struct boss_nightbane : public BossAI
{
public:
boss_nightbane() : CreatureScript("boss_nightbane") { }
CreatureAI* GetAI(Creature* creature) const override
boss_nightbane(Creature* creature) : BossAI(creature, DATA_NIGHTBANE)
{
return GetKarazhanAI<boss_nightbaneAI>(creature);
_intro = true;
_skeletonCount = 5;
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_nightbaneAI : public ScriptedAI
void Reset() override
{
boss_nightbaneAI(Creature* creature) : ScriptedAI(creature)
BossAI::Reset();
_skeletonscheduler.CancelAll();
Phase = 1;
MovePhase = 0;
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetSpeed(MOVE_RUN, 2.0f);
me->SetDisableGravity(_intro);
me->SetWalk(false);
me->setActive(true);
if (instance)
{
instance = creature->GetInstanceScript();
Intro = true;
if (instance->GetData(DATA_NIGHTBANE) == DONE)
me->DisappearAndDie();
else
instance->SetData(DATA_NIGHTBANE, NOT_STARTED);
}
InstanceScript* instance;
HandleTerraceDoors(true);
uint32 Phase;
_flying = false;
_movement = false;
bool RainBones;
bool Skeletons;
uint32 BellowingRoarTimer;
uint32 CharredEarthTimer;
uint32 DistractingAshTimer;
uint32 SmolderingBreathTimer;
uint32 TailSweepTimer;
uint32 RainofBonesTimer;
uint32 SmokingBlastTimer;
uint32 FireballBarrageTimer;
uint32 SearingCindersTimer;
uint32 FlyCount;
uint32 FlyTimer;
bool Intro;
bool Flying;
bool Movement;
uint32 MovePhase;
void Reset() override
if (!_intro)
{
BellowingRoarTimer = 30000;
CharredEarthTimer = 15000;
DistractingAshTimer = 20000;
SmolderingBreathTimer = 10000;
TailSweepTimer = 12000;
RainofBonesTimer = 10000;
SmokingBlastTimer = 20000;
FireballBarrageTimer = 13000;
SearingCindersTimer = 14000;
//when boss is reset and we're past the intro
//cannot despawn, but have to move to a location where he normally is
//me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
Position preSpawnPosis = me->GetHomePosition();
me->NearTeleportTo(preSpawnPosis);
instance->SetData(DATA_NIGHTBANE, NOT_STARTED);
_intro = true;
Phase = 1;
FlyCount = 0;
MovePhase = 0;
}
me->SetSpeed(MOVE_RUN, 2.0f);
me->SetDisableGravity(Intro);
me->SetWalk(false);
me->setActive(true);
ScheduleHealthCheckEvent({25, 50, 70}, [&]{
TakeOff();
});
}
if (instance)
void HandleTerraceDoors(bool open)
{
if (instance)
{
instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_1), open);
instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_2), open);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
if (instance)
instance->SetData(DATA_NIGHTBANE, IN_PROGRESS);
HandleTerraceDoors(false);
Talk(YELL_AGGRO);
ScheduleGround();
}
void ScheduleGround() {
scheduler.Schedule(30s, GROUP_GROUND, [this](TaskContext context)
{
DoCastAOE(SPELL_BELLOWING_ROAR);
context.Repeat(30s, 40s);
}).Schedule(15s, GROUP_GROUND, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_CHARRED_EARTH, 0, 100.0f, true);
context.Repeat(20s);
}).Schedule(10s, GROUP_GROUND, [this](TaskContext context)
{
DoCastVictim(SPELL_SMOLDERING_BREATH);
context.Repeat(20s);
}).Schedule(12s, GROUP_GROUND, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
{
if (instance->GetData(DATA_NIGHTBANE) == DONE)
me->DisappearAndDie();
else
instance->SetData(DATA_NIGHTBANE, NOT_STARTED);
}
HandleTerraceDoors(true);
Flying = false;
Movement = false;
if (!Intro)
{
me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
me->GetMotionMaster()->MoveTargetedHome();
}
}
void HandleTerraceDoors(bool open)
{
if (instance)
{
instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_1), open);
instance->HandleGameObject(instance->GetGuidData(DATA_MASTERS_TERRACE_DOOR_2), open);
}
}
void JustEngagedWith(Unit* /*who*/) override
{
if (instance)
instance->SetData(DATA_NIGHTBANE, IN_PROGRESS);
HandleTerraceDoors(false);
Talk(YELL_AGGRO);
}
void AttackStart(Unit* who) override
{
if (!Intro && !Flying)
ScriptedAI::AttackStart(who);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SetData(DATA_NIGHTBANE, DONE);
HandleTerraceDoors(true);
}
void MoveInLineOfSight(Unit* who) override
{
if (!Intro && !Flying)
ScriptedAI::MoveInLineOfSight(who);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (Intro)
{
if (id >= 8)
if (!me->HasInArc(M_PI, target))
{
Intro = false;
me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
return;
DoCast(target, SPELL_TAIL_SWEEP);
}
}
context.Repeat(15s);
}).Schedule(14s, GROUP_GROUND, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_SEARING_CINDERS);
context.Repeat(10s);
});
}
void ScheduleFly() {
_skeletonSpawnCounter = 0;
scheduler.Schedule(2s, GROUP_FLYING, [this](TaskContext)
{
DoCastVictim(SPELL_RAIN_OF_BONES);
_skeletonscheduler.Schedule(50ms, [this](TaskContext context)
{
//spawns skeletons every second until skeletonCount is reached
if(_skeletonSpawnCounter < _skeletonCount)
{
DoCastVictim(SPELL_SUMMON_SKELETON, true);
_skeletonSpawnCounter++;
context.Repeat(2s);
}
});
}).Schedule(20s, GROUP_FLYING, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_DISTRACTING_ASH);
context.Repeat(2s); //timer wrong?
}).Schedule(25s, GROUP_FLYING, [this](TaskContext context)
{
//5 seconds added due to double trigger?
//trigger for timer in original + in rain of bones
//timers need some investigation
DoCastVictim(SPELL_SMOKING_BLAST);
context.Repeat(1500ms); //timer wrong?
}).Schedule(13s, GROUP_FLYING, [this](TaskContext context)
{
DoCastOnFarAwayPlayers(SPELL_FIREBALL_BARRAGE, false, 80.0f);
context.Repeat(20s);
});
}
void AttackStart(Unit* who) override
{
if (!_intro && !_flying)
ScriptedAI::AttackStart(who);
}
void JustDied(Unit* /*killer*/) override
{
if (instance)
instance->SetData(DATA_NIGHTBANE, DONE);
HandleTerraceDoors(true);
}
void MoveInLineOfSight(Unit* who) override
{
if (!_intro && !_flying)
ScriptedAI::MoveInLineOfSight(who);
}
void MovementInform(uint32 type, uint32 id) override
{
if (type != POINT_MOTION_TYPE)
return;
if (_intro)
{
if (id >= 8)
{
_intro = false;
//me->SetHomePosition(IntroWay[7][0], IntroWay[7][1], IntroWay[7][2], 0);
//doesn't need home position because we have to "despawn" boss on reset
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetInCombatWithZone();
return;
}
MovePhase = id + 1;
return;
}
if (_flying)
{
if (id == 0)
{
Talk(EMOTE_BREATH);
_flying = false;
Phase = 2;
return;
}
if (id < 8)
MovePhase = id + 1;
else
{
Phase = 1;
_flying = false;
_movement = true;
return;
}
}
}
if (Flying)
void JustSummoned(Creature* summon) override
{
summon->AI()->AttackStart(me->GetVictim());
summons.Summon(summon);
}
void DoCastOnFarAwayPlayers(uint32 spellid, bool triggered, float tresholddistance)
{
//resembles DoCastToAllHostilePlayers a bit/lot
ThreatContainer::StorageType targets = me->GetThreatMgr().GetThreatList();
for (ThreatContainer::StorageType::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
{
if (id == 0)
if (unit->IsPlayer() && !unit->IsWithinDist(me, tresholddistance, false))
{
Talk(EMOTE_BREATH);
Flying = false;
Phase = 2;
return;
}
if (id < 8)
MovePhase = id + 1;
else
{
Phase = 1;
Flying = false;
Movement = true;
return;
me->CastSpell(unit, spellid, triggered);
}
}
}
}
void JustSummoned(Creature* summoned) override
void TakeOff()
{
Talk(YELL_FLY_PHASE);
scheduler.CancelGroup(GROUP_GROUND);
me->InterruptSpell(CURRENT_GENERIC_SPELL);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(0, IntroWay[2][0], IntroWay[2][1], IntroWay[2][2]);
_flying = true;
ScheduleFly();
//handle landing again
scheduler.Schedule(45s, 60s, [this](TaskContext)
{
summoned->AI()->AttackStart(me->GetVictim());
}
Talk(YELL_LAND_PHASE);
void TakeOff()
{
Talk(YELL_FLY_PHASE);
me->InterruptSpell(CURRENT_GENERIC_SPELL);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->SetDisableGravity(true);
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(0, IntroWay[2][0], IntroWay[2][1], IntroWay[2][2]);
me->GetMotionMaster()->MovePoint(3, IntroWay[3][0], IntroWay[3][1], IntroWay[3][2]);
Flying = true;
FlyTimer = urand(45000, 60000); //timer wrong between 45 and 60 seconds
++FlyCount;
RainofBonesTimer = 5000; //timer wrong (maybe)
RainBones = false;
Skeletons = false;
}
void UpdateAI(uint32 diff) override
{
if (Intro)
_flying = true;
scheduler.CancelGroup(GROUP_FLYING);
scheduler.Schedule(2s, [this](TaskContext)
{
if (MovePhase)
{
if (MovePhase >= 7)
{
me->SetDisableGravity(false);
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]);
}
else
{
me->GetMotionMaster()->MovePoint(MovePhase, IntroWay[MovePhase][0], IntroWay[MovePhase][1], IntroWay[MovePhase][2]);
}
MovePhase = 0;
}
return;
}
ScheduleGround();
});
});
}
if (Flying && MovePhase)
void UpdateAI(uint32 diff) override
{
if (_intro)
{
if (MovePhase)
{
if (MovePhase >= 7)
{
@@ -282,158 +341,70 @@ public:
me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]);
}
else
{
me->GetMotionMaster()->MovePoint(MovePhase, IntroWay[MovePhase][0], IntroWay[MovePhase][1], IntroWay[MovePhase][2]);
}
MovePhase = 0;
}
if (!UpdateVictim())
return;
if (Flying)
return;
// Phase 1 "GROUND FIGHT"
if (Phase == 1)
{
if (Movement)
{
DoStartMovement(me->GetVictim());
Movement = false;
}
if (BellowingRoarTimer <= diff)
{
DoCastVictim(SPELL_BELLOWING_ROAR);
BellowingRoarTimer = urand(30000, 40000);
}
else
BellowingRoarTimer -= diff;
if (SmolderingBreathTimer <= diff)
{
DoCastVictim(SPELL_SMOLDERING_BREATH);
SmolderingBreathTimer = 20000;
}
else
SmolderingBreathTimer -= diff;
if (CharredEarthTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
DoCast(target, SPELL_CHARRED_EARTH);
CharredEarthTimer = 20000;
}
else
CharredEarthTimer -= diff;
if (TailSweepTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
if (!me->HasInArc(M_PI, target))
DoCast(target, SPELL_TAIL_SWEEP);
TailSweepTimer = 15000;
}
else
TailSweepTimer -= diff;
if (SearingCindersTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
DoCast(target, SPELL_SEARING_CINDERS);
SearingCindersTimer = 10000;
}
else
SearingCindersTimer -= diff;
uint32 Prozent = uint32(me->GetHealthPct());
if (Prozent < 75 && FlyCount == 0) // first take off 75%
TakeOff();
if (Prozent < 50 && FlyCount == 1) // secound take off 50%
TakeOff();
if (Prozent < 25 && FlyCount == 2) // third take off 25%
TakeOff();
DoMeleeAttackIfReady();
}
//Phase 2 "FLYING FIGHT"
if (Phase == 2)
{
if (!RainBones)
{
if (!Skeletons)
{
for (uint8 i = 0; i <= 3; ++i)
{
DoCastVictim(SPELL_SUMMON_SKELETON);
Skeletons = true;
}
}
if (RainofBonesTimer < diff && !RainBones) // only once at the beginning of phase 2
{
DoCastVictim(SPELL_RAIN_OF_BONES);
RainBones = true;
SmokingBlastTimer = 20000;
}
else
RainofBonesTimer -= diff;
if (DistractingAshTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
DoCast(target, SPELL_DISTRACTING_ASH);
DistractingAshTimer = 2000; //timer wrong
}
else
DistractingAshTimer -= diff;
}
if (RainBones)
{
if (SmokingBlastTimer <= diff)
{
DoCastVictim(SPELL_SMOKING_BLAST);
SmokingBlastTimer = 1500; //timer wrong
}
else
SmokingBlastTimer -= diff;
}
if (FireballBarrageTimer <= diff)
{
if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0))
DoCast(target, SPELL_FIREBALL_BARRAGE);
FireballBarrageTimer = 20000;
}
else
FireballBarrageTimer -= diff;
if (FlyTimer <= diff) //landing
{
Talk(YELL_LAND_PHASE);
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(3, IntroWay[3][0], IntroWay[3][1], IntroWay[3][2]);
Flying = true;
}
else
FlyTimer -= diff;
}
return;
}
};
};
if (_flying && MovePhase)
{
if (MovePhase >= 7)
{
me->SetDisableGravity(false);
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]);
}
else
me->GetMotionMaster()->MovePoint(MovePhase, IntroWay[MovePhase][0], IntroWay[MovePhase][1], IntroWay[MovePhase][2]);
MovePhase = 0;
}
if (!UpdateVictim())
return;
if (_flying)
return;
scheduler.Update(diff);
_skeletonscheduler.Update(diff);
// Phase 1 "GROUND FIGHT"
if (Phase == 1)
{
if (_movement)
{
DoStartMovement(me->GetVictim());
_movement = false;
}
DoMeleeAttackIfReady();
}
}
private:
uint32 Phase;
TaskScheduler _skeletonscheduler;
bool _intro;
bool _flying;
bool _movement;
uint32 MovePhase;
uint8 _skeletonCount;
uint8 _skeletonSpawnCounter;
};
class go_blackened_urn : public GameObjectScript
{
public:
go_blackened_urn() : GameObjectScript("go_blackened_urn") { }
//if we summon an entity instead of using a sort of invisible entity, we could unsummon boss on reset
//right now that doesn't work because of how the urn works
bool OnGossipHello(Player* player, GameObject* go) override
{
if (InstanceScript* pInstance = go->GetInstanceScript())
@@ -449,6 +420,6 @@ public:
void AddSC_boss_nightbane()
{
new boss_nightbane();
RegisterKarazhanCreatureAI(boss_nightbane);
new go_blackened_urn();
}

View File

@@ -292,6 +292,7 @@ public:
{
instance->SetData(DATA_ARCHIMONDEEVENT, NOT_STARTED);
me->SetReactState(REACT_AGGRESSIVE);
DoomfireSpiritGUID.Clear();
WorldTreeGUID.Clear();
WispCount = 0;
@@ -618,6 +619,7 @@ public:
break;
}
case EVENT_BELOW_10_PERCENT_HP:
me->SetReactState(REACT_PASSIVE);
DoCastProtection(); // Protection of Elune against Finger and Hand of Death
BelowTenPercent = true;
me->GetMotionMaster()->Clear(false);

View File

@@ -218,9 +218,9 @@ struct boss_magtheridon : public BossAI
Talk(SAY_EMOTE_FREE);
Talk(SAY_FREE);
scheduler.CancelGroup(GROUP_EARLY_RELEASE_CHECK); //cancel regular countdown
_magReleased = true;
scheduler.Schedule(3s, [this](TaskContext)
{
_magReleased = true; //redundancy
ScheduleCombatEvents();
});
}
@@ -239,6 +239,7 @@ struct boss_magtheridon : public BossAI
{
Talk(SAY_EMOTE_FREE);
Talk(SAY_FREE);
_magReleased = true;
}).Schedule(123s, GROUP_EARLY_RELEASE_CHECK, [this](TaskContext /*context*/)
{
ScheduleCombatEvents();

View File

@@ -4884,6 +4884,39 @@ class spell_gen_shriveling_gaze : public AuraScript
}
};
// 38048 - Curse of Pain
enum CurseOfPain
{
SPELL_CURSE_OF_PAIN = 38048,
};
class spell_gen_curse_of_pain : public AuraScript
{
PrepareAuraScript(spell_gen_curse_of_pain);
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_CURSE_OF_PAIN });
}
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
Unit* target = GetTarget();
if (target && target->ToPlayer())
{
if (target->GetHealthPct() < 50.f)
{
target->RemoveAurasDueToSpell(SPELL_CURSE_OF_PAIN);
}
}
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_curse_of_pain::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE);
}
};
void AddSC_generic_spell_scripts()
{
RegisterSpellScript(spell_silithyst);
@@ -5029,4 +5062,5 @@ void AddSC_generic_spell_scripts()
RegisterSpellScript(spell_freezing_circle);
RegisterSpellScript(spell_gen_threshalisk_charge);
RegisterSpellScript(spell_gen_shriveling_gaze);
RegisterSpellScript(spell_gen_curse_of_pain);
}

View File

@@ -974,6 +974,9 @@ class spell_q6124_6129_apply_salve : public SpellScript
if (newEntry)
{
creatureTarget->UpdateEntry(newEntry);
creatureTarget->GetMotionMaster()->Clear();
creatureTarget->GetMotionMaster()->MoveFleeing(caster);
creatureTarget->SetUnitFlag(UNIT_FLAG_NOT_ATTACKABLE_1);
creatureTarget->DespawnOrUnsummon(DESPAWN_TIME);
caster->KilledMonsterCredit(newEntry);
}

View File

@@ -486,7 +486,7 @@ public:
if (creature->IsTrainer())
AddGossipItemFor(player, GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN);
if (player->HasSkill(SKILL_ALCHEMY) && player->GetBaseSkillValue(SKILL_ALCHEMY) >= 350 && player->GetLevel() > 67)
if (player->HasSkill(SKILL_ALCHEMY) && player->GetBaseSkillValue(SKILL_ALCHEMY) >= 325 && player->GetLevel() > 67)
{
if (player->GetQuestRewardStatus(Q_MASTER_TRANSMUTE) || player->GetQuestRewardStatus(Q_MASTER_ELIXIR) || player->GetQuestRewardStatus(Q_MASTER_POTION))
{

View File

@@ -32,6 +32,7 @@
#include "ScriptedGossip.h"
#include "SmartAI.h"
#include "SpellAuras.h"
#include "TaskScheduler.h"
#include "WaypointMgr.h"
#include "World.h"
@@ -2653,6 +2654,32 @@ public:
}
};
struct npc_crashin_thrashin_robot : public ScriptedAI
{
public:
npc_crashin_thrashin_robot(Creature* creature) : ScriptedAI(creature)
{
}
void IsSummonedBy(WorldObject* /*summoner*/) override
{
_scheduler.Schedule(180s, [this](TaskContext /*context*/)
{
me->KillSelf();
});
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
ScriptedAI::UpdateAI(diff);
}
private:
TaskScheduler _scheduler;
};
void AddSC_npcs_special()
{
// Ours
@@ -2680,4 +2707,5 @@ void AddSC_npcs_special()
new npc_spring_rabbit();
new npc_stable_master();
RegisterCreatureAI(npc_arcanite_dragonling);
RegisterCreatureAI(npc_crashin_thrashin_robot);
}