Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2025-04-18 22:57:30 +08:00
28 changed files with 922 additions and 74 deletions

View File

@@ -167,7 +167,7 @@ void AutobroadcastMgr::SendWorldAnnouncement(uint8 textId)
// Get player's locale
LocaleConstant locale = player->GetSession()->GetSessionDbLocaleIndex();
if (!_autobroadcasts.empty())
if (!_autobroadcasts.contains(textId))
return;
std::string_view localizedMessage = ObjectMgr::GetLocaleString(_autobroadcasts[textId], locale);
@@ -188,7 +188,7 @@ void AutobroadcastMgr::SendNotificationAnnouncement(uint8 textId)
// Retrieve player's locale
LocaleConstant locale = player->GetSession()->GetSessionDbLocaleIndex();
if (!_autobroadcasts.count(textId))
if (!_autobroadcasts.contains(textId))
return;
// Get localized message

View File

@@ -4537,7 +4537,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
// trigger update zone for alive state zone updates
uint32 newzone, newarea;
GetZoneAndAreaId(newzone, newarea);
UpdateZone(newzone, newarea);
UpdateZone(newzone, newarea, true);
sOutdoorPvPMgr->HandlePlayerResurrects(this, newzone);
if (Battleground* bg = GetBattleground())

View File

@@ -1852,7 +1852,7 @@ public:
itr->SetPvP(state);
}
void UpdatePvP(bool state, bool _override = false);
void UpdateZone(uint32 newZone, uint32 newArea);
void UpdateZone(uint32 newZone, uint32 newArea, bool force = false);
void UpdateArea(uint32 newArea);
void SetNeedZoneUpdate(bool needUpdate) { m_needZoneUpdate = needUpdate; }

View File

@@ -1250,17 +1250,20 @@ void Player::UpdateArea(uint32 newArea)
RemoveRestFlag(REST_FLAG_IN_FACTION_AREA);
}
void Player::UpdateZone(uint32 newZone, uint32 newArea)
void Player::UpdateZone(uint32 newZone, uint32 newArea, bool force)
{
if (!newZone)
return;
if (m_zoneUpdateId != newZone)
if (m_zoneUpdateId != newZone || force)
{
sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone);
sWorldState->HandlePlayerLeaveZone(this, static_cast<WorldStateZoneId>(m_zoneUpdateId));
sWorldState->HandlePlayerEnterZone(this, static_cast<WorldStateZoneId>(newZone));
}
if (m_zoneUpdateId != newZone)
{
sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId);
sBattlefieldMgr->HandlePlayerEnterZone(this, newZone);
SendInitWorldStates(newZone,

View File

@@ -65,7 +65,10 @@ enum Misc
struct boss_brutallus : public BossAI
{
boss_brutallus(Creature* creature) : BossAI(creature, DATA_BRUTALLUS) { }
boss_brutallus(Creature* creature) : BossAI(creature, DATA_BRUTALLUS)
{
me->SetCorpseDelay(360);
}
void Reset() override
{

View File

@@ -69,10 +69,12 @@ enum Spells
SPELL_BLAZE_SUMMON = 45236
};
enum Misc
enum TwinPhases
{
ACTION_SISTER_DIED = 1,
GROUP_SPECIAL_ABILITY = 1
GROUP_SPECIAL_ABILITY = 1,
GROUP_PYROGENICS = 2,
GROUP_FLAME_SEAR = 3
};
struct boss_sacrolash : public BossAI
@@ -226,16 +228,29 @@ struct boss_alythess : public BossAI
Talk(YELL_SISTER_SACROLASH_DEAD);
me->CastSpell(me, SPELL_EMPOWER, true);
scheduler.CancelGroup(GROUP_SPECIAL_ABILITY);
scheduler.CancelAll();
ScheduleTimedEvent(1s, [&] {
DoCastVictim(SPELL_BLAZE);
}, 3800ms);
scheduler.Schedule(16s, GROUP_PYROGENICS, [this](TaskContext context) {
DoCastSelf(SPELL_PYROGENICS);
context.Repeat(16s, 28s);
});
ScheduleTimedEvent(8s, 10s, [&] {
me->CastCustomSpell(SPELL_FLAME_SEAR, SPELLVALUE_MAX_TARGETS, urand(4, 5), me, TRIGGERED_NONE);
}, 8s, 10s);
ScheduleTimedEvent(20s, 26s, [&] {
Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 100.0f);
if (!target)
target = me->GetVictim();
DoCast(target, SPELL_SHADOW_NOVA);
if (Creature * sacrolash = instance->GetCreature(DATA_SACROLASH))
if (Creature* sacrolash = instance->GetCreature(DATA_SACROLASH))
sacrolash->AI()->Talk(EMOTE_SHADOW_NOVA, target);
}, 20s, 26s);
}
}
@@ -253,10 +268,13 @@ struct boss_alythess : public BossAI
DoCastVictim(SPELL_BLAZE);
}, 3800ms);
ScheduleTimedEvent(21s, 34s, [&] {
// PYROGENICS Phase 1
scheduler.Schedule(21s, GROUP_PYROGENICS, [this](TaskContext context) {
DoCastSelf(SPELL_PYROGENICS);
}, 21s, 34s);
context.Repeat(21s, 34s);
});
// FLAME_SEAR Phase 1
ScheduleTimedEvent(10s, 15s, [&] {
me->CastCustomSpell(SPELL_FLAME_SEAR, SPELLVALUE_MAX_TARGETS, urand(4, 5), me, TRIGGERED_NONE);
}, 10s, 15s);

View File

@@ -85,6 +85,7 @@ enum Misc
GROUP_START_INTRO = 0,
GROUP_BREATH = 1,
GROUP_TAKEOFF = 2,
NPC_FOG_TRIGGER = 23472,
NPC_KALECGOS_FELMYST = 24844, // Same as Magister's Terrace
@@ -214,19 +215,25 @@ struct boss_felmyst : public BossAI
}, 7500ms);
ScheduleTimedEvent(13s, 30s, [&] {
Talk(YELL_BREATH);
DoCastVictim(SPELL_CORROSION);
if (scheduler.GetNextGroupOccurrence(GROUP_TAKEOFF) > 2s)
{
Talk(YELL_BREATH);
DoCastVictim(SPELL_CORROSION);
}
}, 30s, 39s);
ScheduleTimedEvent(18s, 43s, [&] {
DoCastSelf(SPELL_GAS_NOVA);
if (scheduler.GetNextGroupOccurrence(GROUP_TAKEOFF) > 2s)
DoCastSelf(SPELL_GAS_NOVA);
}, 18s, 43s);
ScheduleTimedEvent(26s, 53s, [&] {
DoCastRandomTarget(SPELL_ENCAPSULATE_CHANNEL, 0, 50.0f);
if (scheduler.GetNextGroupOccurrence(GROUP_TAKEOFF) > 9s)
DoCastRandomTarget(SPELL_ENCAPSULATE_CHANNEL, 0, 50.0f);
}, 26s, 53s);
me->m_Events.AddEventAtOffset([&] {
scheduler.Schedule(1min, GROUP_TAKEOFF, [&](TaskContext)
{
Talk(YELL_TAKEOFF);
scheduler.CancelAll();
me->SetReactState(REACT_PASSIVE);
@@ -241,7 +248,7 @@ struct boss_felmyst : public BossAI
SetInvincibility(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
me->GetMotionMaster()->MovePoint(POINT_TAKEOFF, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 20.0f);
}, 1min);
});
}
void MovementInform(uint32 type, uint32 point) override

View File

@@ -324,8 +324,14 @@ struct boss_kiljaeden : public BossAI
});
ScheduleTimedEvent(15s, [&] {
me->RemoveAurasDueToSpell(SPELL_ARMAGEDDON_PERIODIC);
Talk(EMOTE_KJ_DARKNESS);
DoCastAOE(SPELL_DARKNESS_OF_A_THOUSAND_SOULS);
me->m_Events.AddEventAtOffset([this]() {
if (me->IsAlive() && me->IsInCombat())
DoCastSelf(SPELL_ARMAGEDDON_PERIODIC, true);
}, 9s);
}, 45s);
ScheduleTimedEvent(10s, [&] {
@@ -335,6 +341,19 @@ struct boss_kiljaeden : public BossAI
ScheduleHealthCheckEvent(25, [&] {
_phase = PHASE_SACRIFICE;
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_KJ_REFLECTION);
me->CastCustomSpell(SPELL_SINISTER_REFLECTION, SPELLVALUE_MAX_TARGETS, 1, me, TRIGGERED_NONE);
me->CastCustomSpell(SPELL_SINISTER_REFLECTION, SPELLVALUE_MAX_TARGETS, 1, me, TRIGGERED_NONE);
me->CastCustomSpell(SPELL_SINISTER_REFLECTION, SPELLVALUE_MAX_TARGETS, 1, me, TRIGGERED_NONE);
me->CastCustomSpell(SPELL_SINISTER_REFLECTION, SPELLVALUE_MAX_TARGETS, 1, me, TRIGGERED_NONE);
}, 1s);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_SHADOW_SPIKE);
}, 2s);
if (Creature* kalec = instance->GetCreature(DATA_KALECGOS_KJ))
{
kalec->AI()->Talk(SAY_KALECGOS_FOCUS, 8s);
@@ -371,17 +390,15 @@ struct boss_kiljaeden : public BossAI
ScheduleBasicAbilities();
me->m_Events.AddEventAtOffset([&] {
Talk(SAY_KJ_REFLECTION);
me->CastCustomSpell(SPELL_SINISTER_REFLECTION, SPELLVALUE_MAX_TARGETS, 1, me, TRIGGERED_NONE);
me->CastCustomSpell(SPELL_SINISTER_REFLECTION, SPELLVALUE_MAX_TARGETS, 1, me, TRIGGERED_NONE);
me->CastCustomSpell(SPELL_SINISTER_REFLECTION, SPELLVALUE_MAX_TARGETS, 1, me, TRIGGERED_NONE);
me->CastCustomSpell(SPELL_SINISTER_REFLECTION, SPELLVALUE_MAX_TARGETS, 1, me, TRIGGERED_NONE);
}, 1s);
ScheduleTimedEvent(15s, [&] {
ScheduleTimedEvent(25s, [&] {
me->RemoveAurasDueToSpell(SPELL_ARMAGEDDON_PERIODIC);
Talk(EMOTE_KJ_DARKNESS);
DoCastAOE(SPELL_DARKNESS_OF_A_THOUSAND_SOULS);
me->m_Events.AddEventAtOffset([this]() {
if (me->IsAlive() && me->IsInCombat())
DoCastSelf(SPELL_ARMAGEDDON_PERIODIC, true);
}, 9s);
}, 25s);
ScheduleTimedEvent(1500ms, [&] {
@@ -520,7 +537,7 @@ struct boss_kiljaeden : public BossAI
summon->CastSpell(summon, SPELL_ARMAGEDDON_VISUAL, true);
summon->SetPosition(summon->GetPositionX(), summon->GetPositionY(), summon->GetPositionZ() + 20.0f, 0.0f);
summon->m_Events.AddEvent(new CastArmageddon(summon), summon->m_Events.CalculateTime(6000));
summon->DespawnOrUnsummon(10000);
summon->DespawnOrUnsummon(urand(8000, 10000));
}
}
@@ -1082,8 +1099,15 @@ class spell_kiljaeden_armageddon_periodic_aura : public AuraScript
void HandlePeriodic(AuraEffect const* aurEff)
{
PreventDefaultAction();
if (Unit* target = GetUnitOwner()->GetAI()->SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true))
GetUnitOwner()->CastSpell(target, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true);
Unit* caster = GetUnitOwner();
std::list<Creature*> armageddons;
caster->GetCreatureListWithEntryInGrid(armageddons, NPC_ARMAGEDDON_TARGET, 100.0f);
if (armageddons.size() >= 3)
return;
if (Unit* target = caster->GetAI()->SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true))
caster->CastSpell(target, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell, true);
}
void Register() override

View File

@@ -48,10 +48,17 @@ enum Spells
//Black Hole Spells
SPELL_BLACK_HOLE_SUMMON_VISUAL = 46242,
SPELL_BLACK_HOLE_SUMMON_VISUAL2 = 46248,
SPELL_BLACK_HOLE_SUMMON_VISUAL2 = 46247,
SPELL_BLACK_HOLE_VISUAL2 = 46235,
SPELL_BLACK_HOLE_PASSIVE = 46228,
SPELL_BLACK_HOLE_EFFECT = 46230
SPELL_BLACK_HOLE_EFFECT = 46230,
// Dark Fiend Spells
SPELL_DARK_FIEND_APPEARANCE = 45934,
SPELL_DARK_FIEND_SECONDARY = 45936,
SPELL_DARK_FIEND_TRIGGER = 45944
// It is currently unkown why Dark Fiend Casts this or what it should do
//SPELL_DARK_FIEND_TRIGGER_SINGLE = 45943
};
struct boss_muru : public BossAI
@@ -67,6 +74,17 @@ struct boss_muru : public BossAI
me->m_Events.KillAllEvents(false);
}
void MoveInLineOfSight(Unit* who) override
{
// Radius of room is ~38.5f this might need adjusting a bit
// Radius ~36.0 is right inside
// Radius 20.0 is outer circle
if (!me->IsInCombat() && who->IsPlayer() && who->GetPositionZ() > 69.0f && me->IsWithinDistInMap(who, 25.0f))
{
me->SetInCombatWithZone();
}
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
@@ -169,10 +187,81 @@ struct boss_entropius : public ScriptedAI
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
scheduler.Update(diff);
}
};
struct npc_dark_fiend : public ScriptedAI
{
npc_dark_fiend(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
_lastVictimGUID.Clear();
_spellCast = false;
me->SetReactState(REACT_PASSIVE);
DoCast(me, SPELL_DARK_FIEND_APPEARANCE);
DoCast(me, SPELL_DARK_FIEND_SECONDARY);
me->m_Events.AddEventAtOffset([this]() {
me->SetReactState(REACT_AGGRESSIVE);
if (Unit* target = SelectTargetFromPlayerList(200.0f, 0, true))
{
AttackStart(target);
me->AddThreat(target, 100000.0f);
}
}, 1s, 2s);
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*schoolMask*/) override
{
if (damage >= me->GetHealth())
damage = me->GetHealth() - me->GetMaxHealth() * 0.01f;
}
void UpdateAI(uint32 /*diff*/) override
{
if (!me->HasAura(SPELL_DARK_FIEND_APPEARANCE))
{
me->DespawnOrUnsummon();
return;
}
// Check if victim has changed or disappeared
Unit* currentVictim = me->GetVictim();
ObjectGuid currentVictimGUID = currentVictim ? currentVictim->GetGUID() : ObjectGuid::Empty;
if (_lastVictimGUID != currentVictimGUID)
{
// If had a victim before but now it's gone
if (!_lastVictimGUID.IsEmpty() && currentVictimGUID.IsEmpty())
me->DespawnOrUnsummon();
_lastVictimGUID = currentVictimGUID;
}
if (!UpdateVictim())
return;
if (!_spellCast && currentVictim && me->IsWithinMeleeRange(currentVictim, 2.0f))
{
DoCast(me, SPELL_DARK_FIEND_TRIGGER);
_spellCast = true;
me->m_Events.AddEventAtOffset([this]() {
me->DespawnOrUnsummon();
}, 1s);
}
DoMeleeAttackIfReady();
}
private:
ObjectGuid _lastVictimGUID;
bool _spellCast;
};
struct npc_singularity : public NullCreatureAI
{
npc_singularity(Creature* creature) : NullCreatureAI(creature) { }
@@ -180,34 +269,66 @@ struct npc_singularity : public NullCreatureAI
void Reset() override
{
me->DespawnOrUnsummon(18000);
DoCastSelf(SPELL_BLACK_HOLE_SUMMON_VISUAL, true);
DoCastSelf(SPELL_BLACK_HOLE_SUMMON_VISUAL2, true);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_BLACK_HOLE_SUMMON_VISUAL, true);
}, 2s);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_BLACK_HOLE_SUMMON_VISUAL2, true);
}, 4s);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_BLACK_HOLE_SUMMON_VISUAL, true);
}, 6s);
me->m_Events.AddEventAtOffset([&] {
DoCastSelf(SPELL_BLACK_HOLE_VISUAL2, true);
DoCastSelf(SPELL_BLACK_HOLE_PASSIVE, true);
// Start following players after visuals are complete
FindAndFollowTarget();
}, 8s);
me->m_Events.AddEventAtOffset([&] {
me->KillSelf();
}, 17s);
}
me->m_Events.AddEventAtOffset([&] {
me->RemoveAurasDueToSpell(SPELL_BLACK_HOLE_SUMMON_VISUAL2);
DoCastSelf(SPELL_BLACK_HOLE_VISUAL2, true);
DoCastSelf(SPELL_BLACK_HOLE_PASSIVE, true);
}, 3500ms);
scheduler.Schedule(5s, [this](TaskContext context)
void FindAndFollowTarget()
{
scheduler.Schedule(1s, [this](TaskContext context)
{
Player* target = nullptr;
auto const& playerList = me->GetMap()->GetPlayers();
for (auto const& playerRef : playerList)
{
if (Player* player = playerRef.GetSource())
if (me->GetDistance2d(player) < 15.0f && player->GetPositionZ() < 72.0f && player->IsAlive() && !player->HasAura(SPELL_BLACK_HOLE_EFFECT))
{
if (me->IsWithinLOSInMap(player) && player->IsAlive() && !player->HasAura(SPELL_BLACK_HOLE_EFFECT))
{
me->GetMotionMaster()->MovePoint(0, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), false, true);
context.Repeat();
return;
target = player;
break;
}
}
}
context.Repeat(1s);
if (target)
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveFollow(target, 0.0f, 0.0f);
scheduler.Schedule(6s, [this](TaskContext)
{
FindAndFollowTarget();
});
}
else
{
// No valid target found, check again soon
context.Repeat(1s);
}
});
}
@@ -260,7 +381,7 @@ class spell_muru_darkness_aura : public AuraScript
void OnPeriodic(AuraEffect const* aurEff)
{
if (aurEff->GetTickNumber() == 3)
if (aurEff->GetTickNumber() == 2)
for (uint8 i = 0; i < 8; ++i)
GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_SUMMON_DARK_FIEND + i, true);
}
@@ -312,7 +433,9 @@ class spell_entropius_black_hole_effect : public SpellScript
if (target->GetDistance(GetCaster()) < 5.0f)
{
float o = frand(0, 2 * M_PI);
pos.Relocate(GetCaster()->GetPositionX() + 4.0f * cos(o), GetCaster()->GetPositionY() + 4.0f * std::sin(o), GetCaster()->GetPositionZ() + frand(10.0f, 15.0f));
pos.Relocate(GetCaster()->GetPositionX() + 8.0f * cos(o),
GetCaster()->GetPositionY() + 8.0f * std::sin(o),
GetCaster()->GetPositionZ() + frand(2.0f, 5.0f));
}
else
pos.Relocate(GetCaster()->GetPositionX(), GetCaster()->GetPositionY(), GetCaster()->GetPositionZ() + 1.0f);
@@ -352,9 +475,9 @@ class spell_entropius_negative_energy_periodic : public AuraScript
}
};
class spell_muru_blackhole : public SpellScript
class spell_gen_summon_target_floor : public SpellScript
{
PrepareSpellScript(spell_muru_blackhole);
PrepareSpellScript(spell_gen_summon_target_floor);
void ChangeSummonPos(SpellEffIndex /*effIndex*/)
{
@@ -362,7 +485,7 @@ class spell_muru_blackhole : public SpellScript
return;
WorldLocation summonPos = *GetExplTargetDest();
float destZ = summonPos.GetPositionZ() - GetCaster()->GetMapWaterOrGroundLevel(GetCaster()->GetPosition());
float destZ = summonPos.GetPositionZ() - GetCaster()->GetMapWaterOrGroundLevel(summonPos);
Position offset = { 0.0f, 0.0f, -destZ, 0.0f};
summonPos.RelocateOffset(offset);
SetExplTargetDest(summonPos);
@@ -371,7 +494,7 @@ class spell_muru_blackhole : public SpellScript
void Register() override
{
OnEffectHit += SpellEffectFn(spell_muru_blackhole::ChangeSummonPos, EFFECT_0, SPELL_EFFECT_SUMMON);
OnEffectHit += SpellEffectFn(spell_gen_summon_target_floor::ChangeSummonPos, EFFECT_0, SPELL_EFFECT_SUMMON);
}
};
@@ -380,11 +503,12 @@ void AddSC_boss_muru()
RegisterSunwellPlateauCreatureAI(boss_muru);
RegisterSunwellPlateauCreatureAI(boss_entropius);
RegisterSunwellPlateauCreatureAI(npc_singularity);
RegisterSunwellPlateauCreatureAI(npc_dark_fiend);
RegisterSpellScript(spell_muru_summon_blood_elves_periodic_aura);
RegisterSpellScript(spell_muru_darkness_aura);
RegisterSpellScript(spell_entropius_void_zone_visual_aura);
RegisterSpellScript(spell_entropius_black_hole_effect);
RegisterSpellScript(spell_entropius_negative_energy_periodic);
RegisterSpellScript(spell_muru_blackhole);
RegisterSpellScript(spell_gen_summon_target_floor);
}

View File

@@ -282,10 +282,65 @@ private:
uint32 _triggeredSpellId;
};
enum SunbladeArchMageSpells
{
SPELL_ARCANE_EXPLOSION = 46553,
SPELL_BLINK = 28401,
SPELL_FROST_NOVA = 46555
};
struct npc_sunblade_arch_mage : public ScriptedAI
{
npc_sunblade_arch_mage(Creature* creature) : ScriptedAI(creature)
{
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
scheduler.Schedule(6s, 12s, [this](TaskContext context)
{
DoCastAOE(SPELL_ARCANE_EXPLOSION);
context.Repeat(12s, 18s);
});
scheduler.Schedule(8s, 15s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true))
{
DoCast(target, SPELL_BLINK, true);
DoCastAOE(SPELL_FROST_NOVA, true);
}
context.Repeat(20s, 25s);
});
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
DoMeleeAttackIfReady();
}
private:
TaskScheduler scheduler;
};
void AddSC_instance_sunwell_plateau()
{
new instance_sunwell_plateau();
RegisterSpellScript(spell_cataclysm_breath);
RegisterSunwellPlateauCreatureAI(npc_sunblade_arch_mage);
RegisterSunwellPlateauCreatureAI(npc_sunblade_scout);
RegisterSpellScriptWithArgs(spell_sunwell_teleport, "spell_teleport_to_apex_point", SPELL_TELEPORT_TO_APEX_POINT);
RegisterSpellScriptWithArgs(spell_sunwell_teleport, "spell_teleport_to_witchs_sanctum", SPELL_TELEPORT_TO_WITCHS_SANCTUM);

View File

@@ -710,16 +710,23 @@ public:
switch (eventId)
{
case EVENT_ADD_ARCANE_CHAINS:
if (Player* summoner = me->ToTempSummon()->GetSummonerUnit()->ToPlayer())
if (TempSummon* tempSummon = me->ToTempSummon())
{
summoner->CastSpell(summoner, SPELL_ARCANE_CHAINS_CHANNEL_II, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS & ~TRIGGERED_IGNORE_CAST_ITEM & ~TRIGGERED_IGNORE_POWER_AND_REAGENT_COST & ~TRIGGERED_IGNORE_GCD));
_events.ScheduleEvent(EVENT_FOLLOW_PLAYER, 1s);
if (Unit* summoner = tempSummon->GetSummonerUnit())
{
summoner->CastSpell(summoner, SPELL_ARCANE_CHAINS_CHANNEL_II, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS & ~TRIGGERED_IGNORE_CAST_ITEM & ~TRIGGERED_IGNORE_POWER_AND_REAGENT_COST & ~TRIGGERED_IGNORE_GCD));
_events.ScheduleEvent(EVENT_FOLLOW_PLAYER, 1s);
}
}
break;
case EVENT_FOLLOW_PLAYER:
if (Player* summoner = me->ToTempSummon()->GetSummonerUnit()->ToPlayer())
if (TempSummon* tempSummon = me->ToTempSummon())
{
StartFollow(summoner);
if (Player* summoner = tempSummon->GetSummonerUnit()->ToPlayer())
{
StartFollow(summoner);
}
}
break;
}

View File

@@ -979,37 +979,40 @@ class spell_mage_fingers_of_frost_proc_aura : public AuraScript
{
_chance = 100.f;
_spell = eventInfo.GetProcSpell();
_procSpellDelayMoment = std::nullopt;
if (!_spell || _spell->GetDelayMoment() <= 0)
{
PreventDefaultAction();
}
if (_spell)
_procSpellDelayMoment = _spell->GetDelayMoment();
}
else
{
if (eventInfo.GetSpellPhaseMask() == PROC_SPELL_PHASE_FINISH || ((_spell && _spell->GetDelayMoment() > 0) || !eventInfo.GetDamageInfo()))
{
if (eventInfo.GetSpellPhaseMask() == PROC_SPELL_PHASE_FINISH || (_procSpellDelayMoment.value_or(0) > 0 || !eventInfo.GetDamageInfo()))
PreventDefaultAction();
}
_chance = 0.f;
_spell = nullptr;
ResetProcState();
}
}
void HandleAfterEffectProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo)
{
if (eventInfo.GetSpellPhaseMask() == PROC_SPELL_PHASE_HIT)
switch (eventInfo.GetSpellPhaseMask())
{
_chance = 100.f;
}
else if (eventInfo.GetSpellPhaseMask() == PROC_SPELL_PHASE_FINISH)
{
_chance = 0.f;
_spell = nullptr;
case PROC_SPELL_PHASE_HIT: _chance = 100.f; break;
case PROC_SPELL_PHASE_FINISH: ResetProcState(); break;
default: break;
}
}
void ResetProcState()
{
_chance = 0.f;
_spell = nullptr;
_procSpellDelayMoment = std::nullopt;
}
void Register()
{
DoCheckProc += AuraCheckProcFn(spell_mage_fingers_of_frost_proc_aura::CheckProc);
@@ -1019,10 +1022,15 @@ class spell_mage_fingers_of_frost_proc_aura : public AuraScript
}
public:
// May point to a deleted object.
// Dereferencing is unsafe unless validity is guaranteed by the caller.
Spell const* GetProcSpell() const { return _spell; }
private:
float _chance = 0.f;
std::optional<uint64> _procSpellDelayMoment = std::nullopt;
// May be dangling; points to memory that might no longer be valid.
Spell const* _spell = nullptr;
};