Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-09-29 23:22:47 +08:00
74 changed files with 806 additions and 146 deletions
@@ -28,7 +28,6 @@ EndScriptData */
#include "CommandScript.h"
#include "DBCStores.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Player.h"
@@ -29,7 +29,6 @@
#include "SpellInfo.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "Vehicle.h"
/*######
## npc_eye_of_acherus
@@ -2417,6 +2417,8 @@ public:
ObjectGuid _grabbedPlayer;
bool didbelow50pct;
bool dropped;
bool grabbed;
float _lastSpeed;
InstanceScript* _instance;
bool IsHeroic() { return me->GetMap()->IsHeroic(); }
@@ -2547,6 +2549,8 @@ public:
if (me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED))
return;
HandleSpeedChangeIfNeeded();
switch (_events.ExecuteEvent())
{
case EVENT_GRAB_PLAYER:
@@ -2557,6 +2561,8 @@ public:
}
break;
case EVENT_MOVE_TO_DROP_POS:
grabbed = true;
_lastSpeed = me->GetSpeed(MOVE_WALK);
me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD);
me->SetCanFly(false);
me->SetDisableGravity(false);
@@ -2596,6 +2602,24 @@ public:
break;
}
}
// For some reason, when the Valkyr has a slowdown effect, the speed of PointMovementGenerator
// and the speed on the client side differ, which leads to a "teleportation" effect when a stun aura is applied.
// Restarting the motion master on speed change ensures the movement is synced between the server and client.
void HandleSpeedChangeIfNeeded()
{
if (!grabbed || dropped)
return;
if (me->GetSpeed(MOVE_WALK) == _lastSpeed)
return;
_lastSpeed = me->GetSpeed(MOVE_WALK);
me->GetMotionMaster()->Clear();
me->StopMovingOnCurrentPos();
_events.ScheduleEvent(EVENT_MOVE_TO_DROP_POS, 0);
}
};
CreatureAI* GetAI(Creature* creature) const override
@@ -27,6 +27,7 @@
#include "PassiveAI.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "SmartAI.h"
#include "SpellAuraEffects.h"
#include "SpellScriptLoader.h"
@@ -26,7 +26,6 @@
#include "PassiveAI.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
@@ -24,7 +24,6 @@
#include "SpellAuras.h"
#include "SpellScript.h"
#include "SpellScriptLoader.h"
#include "Vehicle.h"
#include "ulduar.h"
#define SPELL_FLAME_JETS_10 62680
@@ -21,7 +21,6 @@
#include "Player.h"
#include "ScriptedCreature.h"
#include "Transport.h"
#include "Vehicle.h"
#include "WorldPacket.h"
#include "ulduar.h"
@@ -136,7 +136,7 @@ struct boss_gurtogg_bloodboil : public BossAI
bool CanAIAttack(Unit const* who) const override
{
return !who->IsImmunedToDamage(SPELL_SCHOOL_MASK_ALL);
return !who->IsImmunedToDamage(SPELL_SCHOOL_MASK_ALL) && !who->HasUnitState(UNIT_STATE_CONFUSED);
}
void KilledUnit(Unit* /*victim*/) override
@@ -196,6 +196,17 @@ class spell_mother_shahraz_saber_lash_aura : public AuraScript
}
};
const Position validTeleportStairsPos[4] =
{
{966.87f, 184.45f, 192.84f},
{927.22f, 187.04f, 192.84f},
{922.54f, 110.09f, 192.84f},
{958.01f, 110.47f, 192.84f}
};
constexpr float minTeleportDist = 30.f;
constexpr float maxTeleportDist = 50.f;
class spell_mother_shahraz_fatal_attraction : public SpellScript
{
PrepareSpellScript(spell_mother_shahraz_fatal_attraction);
@@ -212,7 +223,44 @@ class spell_mother_shahraz_fatal_attraction : public SpellScript
void SetDest(SpellDestination& dest)
{
dest.Relocate(GetCaster()->GetRandomNearPosition(50.0f));
Position finalDest;
// Check if the boss is near stairs to avoid players falling through the platform with random teleports.
if (GetCaster()->GetPositionY() < 194.f)
finalDest = validTeleportStairsPos[urand(0, 3)];
else
{
finalDest = GetCaster()->GetNearPosition(frand(minTeleportDist, maxTeleportDist), static_cast<float>(rand_norm()) * static_cast<float>(2 * M_PI), true);
// Maybe not necessary but just in case to avoid LOS issues with an object
if (!GetCaster()->IsWithinLOS(finalDest.GetPositionX(), finalDest.GetPositionY(), finalDest.GetPositionZ()))
finalDest = GetCaster()->GetNearPosition(frand(minTeleportDist, maxTeleportDist), static_cast<float>(rand_norm()) * static_cast<float>(2 * M_PI), true);
/* @note: To avoid teleporting players near a walls, we will define a safe area.
* As the boss have an area boudary around y: 320.f. We will limit the safe area to this value, avoiding wird issues.
* x limit: 932.f/960.f | y limit: 224.f/320.f
*/
if (finalDest.m_positionX < 932.f)
finalDest.m_positionX = 932.f;
else if (finalDest.m_positionX > 960.f)
finalDest.m_positionX = 960.f;
if (finalDest.m_positionY < 224.f)
finalDest.m_positionY = 224.f;
else if (finalDest.m_positionY > 320.f)
finalDest.m_positionY = 320.f;
// After relocate a finalDest outside the safe area, we need to recheck the distance with the boss
if (GetCaster()->GetExactDist2d(finalDest) < minTeleportDist)
{
if (finalDest.m_positionX == 932.f || finalDest.m_positionX == 960.f)
finalDest.m_positionY = finalDest.m_positionY + (minTeleportDist - GetCaster()->GetExactDist2d(finalDest));
else if (finalDest.m_positionY == 224.f || finalDest.m_positionY == 320.f)
finalDest.m_positionX = finalDest.m_positionX + (minTeleportDist - GetCaster()->GetExactDist2d(finalDest));
}
}
dest.Relocate(finalDest);
}
void HandleTeleportUnits(SpellEffIndex /*effIndex*/)
@@ -235,7 +283,13 @@ class spell_mother_shahraz_fatal_attraction_dummy : public SpellScript
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return ValidateSpellInfo({ SPELL_FATAL_ATTRACTION_DAMAGE });
return ValidateSpellInfo({ SPELL_FATAL_ATTRACTION_DAMAGE, SPELL_FATAL_ATTRACTION_AURA });
}
void FilterTargets(std::list<WorldObject*>& targets)
{
if (targets.empty())
GetCaster()->RemoveAurasDueToSpell(SPELL_FATAL_ATTRACTION_AURA);
}
void HandleDummy(SpellEffIndex /*effIndex*/)
@@ -255,6 +309,7 @@ class spell_mother_shahraz_fatal_attraction_dummy : public SpellScript
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mother_shahraz_fatal_attraction_dummy::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY);
OnEffectHitTarget += SpellEffectFn(spell_mother_shahraz_fatal_attraction_dummy::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
@@ -178,6 +178,13 @@ public:
for (ObjectGuid const& guid : ashtongueGUIDs)
if (Creature* ashtongue = instance->GetCreature(guid))
ashtongue->SetFaction(FACTION_ASHTONGUE_DEATHSWORN);
[[fallthrough]];
case DATA_TERON_GOREFIEND:
case DATA_GURTOGG_BLOODBOIL:
case DATA_RELIQUARY_OF_SOULS:
if (AllBossesDone({ DATA_SHADE_OF_AKAMA, DATA_TERON_GOREFIEND, DATA_GURTOGG_BLOODBOIL, DATA_RELIQUARY_OF_SOULS }))
if (Creature* trigger = GetCreature(DATA_BLACK_TEMPLE_TRIGGER))
trigger->AI()->Talk(EMOTE_LOWER_TEMPLE_DEFEATED);
break;
case DATA_ILLIDARI_COUNCIL:
if (Creature* akama = GetCreature(DATA_AKAMA_ILLIDAN))
@@ -186,10 +193,6 @@ public:
default:
break;
}
if (AllBossesDone({ DATA_SHADE_OF_AKAMA, DATA_TERON_GOREFIEND, DATA_GURTOGG_BLOODBOIL, DATA_RELIQUARY_OF_SOULS }))
if (Creature* trigger = GetCreature(DATA_BLACK_TEMPLE_TRIGGER))
trigger->AI()->Talk(EMOTE_LOWER_TEMPLE_DEFEATED);
}
return true;
}
@@ -19,7 +19,6 @@
#define DEF_BLOOD_FURNACE_H
#include "CreatureAIImpl.h"
#include "Player.h"
#define DataHeader "BF"
@@ -92,7 +92,7 @@ enum KTSpells
SPELL_KAEL_EXPLODES4 = 36354,
SPELL_KAEL_EXPLODES5 = 36092,
SPELL_GROW = 36184,
SPELL_KEAL_STUNNED = 36185,
SPELL_KAEL_STUNNED = 36185,
SPELL_KAEL_FULL_POWER = 36187,
SPELL_FLOATING_DROWNED = 36550,
SPELL_DARK_BANISH_STATE = 52241, // wrong visual apparently
@@ -153,8 +153,7 @@ enum KTMisc
{
POINT_MIDDLE = 1,
POINT_AIR = 2,
POINT_LAND = 3,
POINT_START_LAST_PHASE = 4,
POINT_START_LAST_PHASE = 3,
DATA_RESURRECT_CAST = 1,
@@ -191,7 +190,9 @@ enum KTTransitionScene
EVENT_SCENE_13 = 62,
EVENT_SCENE_14 = 63,
EVENT_SCENE_15 = 64,
EVENT_SCENE_16 = 65
EVENT_SCENE_16 = 65,
EVENT_SCENE_17 = 66,
EVENT_SCENE_18 = 67
};
enum KTActions
@@ -280,7 +281,7 @@ struct boss_kaelthas : public BossAI
_phase = PHASE_NONE;
_transitionSceneReached = false;
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_HOVER, true);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_HOVER, true); // hover effect 36550 - Floating Drowned
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
SetRoomState(GO_STATE_READY);
@@ -405,13 +406,17 @@ struct boss_kaelthas : public BossAI
void MovementInform(uint32 type, uint32 point) override
{
if (type != POINT_MOTION_TYPE)
if (type != POINT_MOTION_TYPE && type != EFFECT_MOTION_TYPE)
return;
if (point == POINT_MIDDLE)
{
ExecuteMiddleEvent();
}
else if (point == POINT_AIR)
{
me->SetDisableGravity(true, false, false); // updating AnimationTier will break drowning animation later
}
else if (point == POINT_START_LAST_PHASE)
{
me->SetDisableGravity(false);
@@ -479,6 +484,8 @@ struct boss_kaelthas : public BossAI
void ExecuteMiddleEvent()
{
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAllAttackers();
scheduler.ClearValidator();
me->SetTarget();
me->SetFacingTo(M_PI);
@@ -498,7 +505,7 @@ struct boss_kaelthas : public BossAI
trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false);
me->SetDisableGravity(true);
me->SendMovementFlagUpdate();
me->GetMotionMaster()->MoveTakeoff(POINT_AIR, me->GetPositionX(), me->GetPositionY(), 75.0f, 2.99);
me->GetMotionMaster()->MoveTakeoff(POINT_AIR, me->GetPositionX(), me->GetPositionY(), 75.0f, 2.99, true); // AnimType Movement::ToFly does not exist for Kael
DoCastSelf(SPELL_GROW, true);
}, EVENT_SCENE_3);
ScheduleUniqueTimedEvent(7000ms, [&]
@@ -538,8 +545,6 @@ struct boss_kaelthas : public BossAI
me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA2);
me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA3);
DoCastSelf(SPELL_KAEL_EXPLODES5, true);
DoCastSelf(SPELL_FLOATING_DROWNED);
//me->CastSpell(me, SPELL_KEAL_STUNNED, true);
}, EVENT_SCENE_8);
ScheduleUniqueTimedEvent(22000ms, [&]
{
@@ -580,25 +585,34 @@ struct boss_kaelthas : public BossAI
if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()-5, me->GetPositionY()+5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true);
}, EVENT_SCENE_14);
ScheduleUniqueTimedEvent(30500ms, [&]
{
me->SetFacingTo(M_PI);
me->RemoveAurasDueToSpell(SPELL_FLOATING_DROWNED);
me->CastStop();
}, EVENT_SCENE_15);
ScheduleUniqueTimedEvent(30700ms, [&]
{
me->CastStop();
DoCastSelf(SPELL_KAEL_FULL_POWER);
}, EVENT_SCENE_16);
ScheduleUniqueTimedEvent(32000ms, [&]
{
me->RemoveAurasDueToSpell(SPELL_FLOATING_DROWNED);
me->RemoveAurasDueToSpell(SPELL_KEAL_STUNNED);
DoCastSelf(SPELL_KAEL_FULL_POWER);
DoCastSelf(SPELL_KAEL_PHASE_TWO, true);
DoCastSelf(SPELL_PURE_NETHER_BEAM4, true);
DoCastSelf(SPELL_PURE_NETHER_BEAM5, true);
DoCastSelf(SPELL_PURE_NETHER_BEAM6, true);
}, EVENT_SCENE_15);
}, EVENT_SCENE_17);
ScheduleUniqueTimedEvent(36000ms, [&]
{
summons.DespawnEntry(WORLD_TRIGGER);
me->CastStop();
me->GetMotionMaster()->Clear();
me->RemoveAurasDueToSpell(SPELL_DARK_BANISH_STATE); // WRONG VISUAL
me->GetMotionMaster()->MoveLand(POINT_LAND, me->GetPositionX(), me->GetPositionY(), 48.0f, 2.99f); // Moveland doesn't handle POINT_START_LAST_PHASE so we need to use MovePoint
me->GetMotionMaster()->MovePoint(POINT_START_LAST_PHASE, me->GetHomePosition(), false, true);
}, EVENT_SCENE_16);
me->SetDisableGravity(true);
me->SendMovementFlagUpdate();
me->GetMotionMaster()->MoveLand(POINT_START_LAST_PHASE, me->GetHomePosition(), 2.99f);
}, EVENT_SCENE_18);
}
void IntroduceNewAdvisor(KTYells talkIntroduction, KTActions kaelAction)
@@ -689,21 +703,10 @@ struct boss_kaelthas : public BossAI
{
_transitionSceneReached = true;
scheduler.CancelAll();
me->AttackStop();
me->CastStop();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_PASSIVE);
me->GetMotionMaster()->MovePoint(POINT_MIDDLE, me->GetHomePosition(), true, true);
me->ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
me->SendMeleeAttackStop();
ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList();
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
{
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
{
target->AttackStop();
}
}
}
});
ScheduleTimedEvent(1000ms, [&]
@@ -1349,6 +1352,27 @@ class spell_kaelthas_remove_enchanted_weapons : public SpellScript
}
};
// 36092 - Kael Explodes
class spell_kaelthas_kael_explodes : public SpellScript
{
PrepareSpellScript(spell_kaelthas_kael_explodes);
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
{
Unit* caster = GetCaster();
caster->CastSpell((Unit*)nullptr, SPELL_FLOATING_DROWNED, true);
// caster->CastSpell((Unit*)nullptr, SPELL_KAEL_STUNNED, true);
caster->PlayDirectSound(3320);
caster->PlayDirectSound(10845);
caster->PlayDirectSound(6539);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_kaelthas_kael_explodes::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
}
};
void AddSC_boss_kaelthas()
{
RegisterTheEyeAI(boss_kaelthas);
@@ -1368,4 +1392,5 @@ void AddSC_boss_kaelthas()
RegisterSpellScript(spell_kaelthas_summon_nether_vapor);
RegisterSpellScript(spell_kael_pyroblast);
RegisterSpellScript(spell_kaelthas_remove_enchanted_weapons);
RegisterSpellScript(spell_kaelthas_kael_explodes);
}
@@ -375,16 +375,6 @@ class spell_gen_bg_preparation : public AuraScript
{
PrepareAuraScript(spell_gen_bg_preparation)
void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
GetTarget()->ApplySpellImmune(GetId(), IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, true);
}
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
GetTarget()->ApplySpellImmune(GetId(), IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ALL, false);
}
void CalcPeriodic(AuraEffect const* /*effect*/, bool& isPeriodic, int32& amplitude)
{
isPeriodic = true;
@@ -405,9 +395,6 @@ class spell_gen_bg_preparation : public AuraScript
void Register() override
{
OnEffectApply += AuraEffectApplyFn(spell_gen_bg_preparation::HandleEffectApply, EFFECT_0, SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT, AURA_EFFECT_HANDLE_REAL);
OnEffectRemove += AuraEffectRemoveFn(spell_gen_bg_preparation::HandleEffectRemove, EFFECT_0, SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT, AURA_EFFECT_HANDLE_REAL);
DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_gen_bg_preparation::CalcPeriodic, EFFECT_0, SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT);
OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_gen_bg_preparation::Update, EFFECT_0, SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT);
}
+41
View File
@@ -4051,6 +4051,46 @@ class spell_item_gor_dreks_ointment : public SpellScript
}
};
enum Skettis
{
QUEST_FIRES_OVER_SKETTIS = 11008
};
class spell_item_skyguard_blasting_charges : public SpellScript
{
PrepareSpellScript(spell_item_skyguard_blasting_charges);
void HandleOpenObject(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
if (GameObject* go = GetHitGObj())
go->UseDoorOrButton();
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
if (Unit* kaliri = GetHitUnit())
kaliri->ToCreature()->DespawnOrUnsummon(0s, 30s);
}
SpellCastResult CheckQuest()
{
if (Player* playerCaster = GetCaster()->ToPlayer())
{
if (playerCaster->GetQuestStatus(QUEST_FIRES_OVER_SKETTIS) == QUEST_STATUS_INCOMPLETE)
return SPELL_CAST_OK;
}
return SPELL_FAILED_DONT_REPORT;
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_item_skyguard_blasting_charges::HandleOpenObject, EFFECT_1, SPELL_EFFECT_ACTIVATE_OBJECT);
OnEffectHitTarget += SpellEffectFn(spell_item_skyguard_blasting_charges::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
OnCheckCast += SpellCheckCastFn(spell_item_skyguard_blasting_charges::CheckQuest);
}
};
void AddSC_item_spell_scripts()
{
RegisterSpellScript(spell_item_massive_seaforium_charge);
@@ -4175,4 +4215,5 @@ void AddSC_item_spell_scripts()
RegisterSpellAndAuraScriptPair(spell_item_eye_of_grillok, spell_item_eye_of_grillok_aura);
RegisterSpellScript(spell_item_fel_mana_potion);
RegisterSpellScript(spell_item_gor_dreks_ointment);
RegisterSpellScript(spell_item_skyguard_blasting_charges);
}
-1
View File
@@ -26,7 +26,6 @@
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "Spell.h"
#include "WorldSession.h"
// Ours
/*######