From c513da85c892cc68a0ac424d8914a5531ffc8786 Mon Sep 17 00:00:00 2001 From: Dan <83884799+elthehablo@users.noreply.github.com> Date: Tue, 6 Feb 2024 17:47:37 +0100 Subject: [PATCH 01/17] fix(Scripts/SSC): some Vashj fixes (#18276) * initial * script tainted elemental * sql * codestyle --- .../updates/pending_db_world/tainted_ele.sql | 4 ++ .../SerpentShrine/boss_lady_vashj.cpp | 44 ++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 data/sql/updates/pending_db_world/tainted_ele.sql diff --git a/data/sql/updates/pending_db_world/tainted_ele.sql b/data/sql/updates/pending_db_world/tainted_ele.sql new file mode 100644 index 000000000..fb023e085 --- /dev/null +++ b/data/sql/updates/pending_db_world/tainted_ele.sql @@ -0,0 +1,4 @@ +-- +UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_tainted_elemental' WHERE `entry` = 22009; + +DELETE FROM `smart_scripts` WHERE `entryorguid` = 22009 AND `source_type` = 0; diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp index fb0061340..2f48c1338 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp @@ -57,7 +57,9 @@ enum Spells SPELL_SUMMON_SPOREBAT2 = 38490, SPELL_SUMMON_SPOREBAT3 = 38492, SPELL_SUMMON_SPOREBAT4 = 38493, - SPELL_TOXIC_SPORES = 38574 + SPELL_TOXIC_SPORES = 38574, + + SPELL_POISON_BOLT = 38253 }; enum Misc @@ -91,6 +93,8 @@ struct boss_lady_vashj : public BossAI ScheduleHealthCheckEvent(70, [&]{ Talk(SAY_PHASE2); + scheduler.CancelAll(); + me->CastStop(); me->SetReactState(REACT_PASSIVE); me->GetMotionMaster()->MovePoint(POINT_HOME, me->GetHomePosition().GetPositionX(), me->GetHomePosition().GetPositionY(), me->GetHomePosition().GetPositionZ(), true, true); }); @@ -178,7 +182,6 @@ struct boss_lady_vashj : public BossAI me->AddUnitState(UNIT_STATE_ROOT); me->SetFacingTo(me->GetHomePosition().GetOrientation()); instance->SetData(DATA_ACTIVATE_SHIELD, 0); - scheduler.CancelAll(); scheduler.Schedule(2400ms, [this](TaskContext context) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) @@ -271,6 +274,42 @@ private: std::chrono::seconds _batTimer; }; +struct npc_tainted_elemental : public ScriptedAI +{ + npc_tainted_elemental(Creature* creature) : ScriptedAI(creature) { } + + void Reset() override + { + scheduler.CancelAll(); + me->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + me->AddThreat(target, 1000.0f); + } + } + + void JustEngagedWith(Unit* /*who*/) override + { + scheduler.Schedule(100ms, 500ms, [this](TaskContext context) + { + DoCastVictim(SPELL_POISON_BOLT); + context.Repeat(2350ms, 2650ms); + }).Schedule(15s, [this](TaskContext) + { + me->DespawnOrUnsummon(); + }); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + scheduler.Update(diff); + } + +}; + class spell_lady_vashj_magic_barrier : public AuraScript { PrepareAuraScript(spell_lady_vashj_magic_barrier); @@ -410,6 +449,7 @@ class spell_lady_vashj_summons : public SpellScript void AddSC_boss_lady_vashj() { RegisterSerpentShrineAI(boss_lady_vashj); + RegisterSerpentShrineAI(npc_tainted_elemental); RegisterSpellScript(spell_lady_vashj_magic_barrier); RegisterSpellScript(spell_lady_vashj_remove_tainted_cores); RegisterSpellScript(spell_lady_vashj_summon_sporebat); From e2f1648941ad48cccaef777ca2ecf63806b629e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 6 Feb 2024 16:48:31 +0000 Subject: [PATCH 02/17] chore(DB): import pending files Referenced commit(s): c513da85c892cc68a0ac424d8914a5531ffc8786 --- .../tainted_ele.sql => db_world/2024_02_06_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/tainted_ele.sql => db_world/2024_02_06_00.sql} (81%) diff --git a/data/sql/updates/pending_db_world/tainted_ele.sql b/data/sql/updates/db_world/2024_02_06_00.sql similarity index 81% rename from data/sql/updates/pending_db_world/tainted_ele.sql rename to data/sql/updates/db_world/2024_02_06_00.sql index fb023e085..eceb8cd38 100644 --- a/data/sql/updates/pending_db_world/tainted_ele.sql +++ b/data/sql/updates/db_world/2024_02_06_00.sql @@ -1,3 +1,4 @@ +-- DB update 2024_02_05_04 -> 2024_02_06_00 -- UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_tainted_elemental' WHERE `entry` = 22009; From b8118fa06b7444bcf7cbb9049f6b82fc50e914f0 Mon Sep 17 00:00:00 2001 From: Dan <83884799+elthehablo@users.noreply.github.com> Date: Thu, 8 Feb 2024 03:42:20 +0100 Subject: [PATCH 03/17] fix(Scripts/TheEye): Re-factor Kael'thas (#18272) * restructuring base files and instance files * small fix * some fixes * fix * some fixes * up until kael phase * weapon despawn fix * final for now * some minor details * fix weapon despawn * fix by clearing validator still has debug statements to check how far we get * finalise with stop casting * codestyle * remove redundant delay --- .../Outland/TempestKeep/Eye/boss_kaelthas.cpp | 1125 +++++++++-------- .../TempestKeep/Eye/instance_the_eye.cpp | 25 +- .../scripts/Outland/TempestKeep/Eye/the_eye.h | 8 +- 3 files changed, 620 insertions(+), 538 deletions(-) diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 4971d5c70..4fa9f1a27 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -55,7 +55,7 @@ enum Yells enum Spells { - // Phase 2 spells + // _phase 2 spells SPELL_SUMMON_WEAPONS = 36976, SPELL_SUMMON_WEAPONA = 36958, SPELL_SUMMON_WEAPONB = 36959, @@ -65,10 +65,10 @@ enum Spells SPELL_SUMMON_WEAPONF = 36963, SPELL_SUMMON_WEAPONG = 36964, - // Phase 3 spells + // _phase 3 spells SPELL_RESURRECTION = 36450, - // Phase 4 spells + // _phase 4 spells SPELL_FIREBALL = 36805, SPELL_ARCANE_DISRUPTION = 36834, SPELL_PHOENIX = 36723, @@ -94,12 +94,17 @@ enum Spells SPELL_KEAL_STUNNED = 36185, SPELL_KAEL_FULL_POWER = 36187, SPELL_FLOATING_DROWNED = 36550, + SPELL_DARK_BANISH_STATE = 52241, // wrong visual apparently + SPELL_ARCANE_EXPLOSION_VISUAL = 34807, SPELL_PURE_NETHER_BEAM1 = 36196, SPELL_PURE_NETHER_BEAM2 = 36197, SPELL_PURE_NETHER_BEAM3 = 36198, + SPELL_PURE_NETHER_BEAM4 = 36201, + SPELL_PURE_NETHER_BEAM5 = 36290, + SPELL_PURE_NETHER_BEAM6 = 36291, - // Phase 5 spells + // _phase 5 spells SPELL_GRAVITY_LAPSE = 35941, SPELL_GRAVITY_LAPSE_TELEPORT1 = 35966, SPELL_GRAVITY_LAPSE_KNOCKBACK = 34480, @@ -197,6 +202,21 @@ enum Misc EVENT_SCENE_16 = 65 }; +enum KaelActions +{ + ACTION_START_SANGUINAR = 0, + ACTION_START_CAPERNIAN = 1, + ACTION_START_TELONICUS = 2, + ACTION_START_WEAPONS = 3 +}; + +enum SpellGroups +{ + GROUP_PYROBLAST = 0, + GROUP_SHOCK_BARRIER = 1, + GROUP_NETHER_BEAM = 2 +}; + const Position triggersPos[6] = { {799.11f, -38.95f, 85.0f, 0.0f}, @@ -207,549 +227,531 @@ const Position triggersPos[6] = {843.35f, 6.35f, 67.14f, 0.0f} }; -class boss_kaelthas : public CreatureScript +struct boss_kaelthas : public BossAI { -public: - boss_kaelthas() : CreatureScript("boss_kaelthas") { } + boss_kaelthas(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { } - struct boss_kaelthasAI : public BossAI + void PrepareAdvisors() { - boss_kaelthasAI(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { } - - uint8 phase; - EventMap events2; - - void PrepareAdvisors() + for (uint8 advisorData = DATA_THALADRED; advisorData <= DATA_TELONICUS; ++advisorData) { - for (uint8 i = DATA_KAEL_ADVISOR1; i <= DATA_KAEL_ADVISOR4; ++i) - if (Creature* advisor = ObjectAccessor::GetCreature(*me, instance->GetGuidData(i))) - { - advisor->Respawn(true); - advisor->StopMovingOnCurrentPos(); - advisor->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - advisor->SetReactState(REACT_PASSIVE); - summons.Summon(advisor); - } - } - - void SetData(uint32 type, uint32 data) override - { - if (type == DATA_RESURRECT_CAST && data == DATA_RESURRECT_CAST) + if (Creature* advisor = instance->GetCreature(advisorData)) { - for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i) - if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) - if (summon->GetSpawnId()) - { - summon->SetReactState(REACT_PASSIVE); - summon->setDeathState(DeathState::JustRespawned); - summon->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - } + advisor->Respawn(true); + advisor->StopMovingOnCurrentPos(); + advisor->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + advisor->SetReactState(REACT_PASSIVE); + summons.Summon(advisor); } } + } - void SetRoomState(GOState state) + void SetData(uint32 type, uint32 data) override + { + if (type == DATA_RESURRECT_CAST && data == DATA_RESURRECT_CAST) { - if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_BRIDGE_WINDOW))) - window->SetGoState(state); - if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_KAEL_STATUE_RIGHT))) - window->SetGoState(state); - if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_KAEL_STATUE_LEFT))) - window->SetGoState(state); + summons.DoForAllSummons([&](WorldObject* summon){ + if (Creature* summonedCreature = summon->ToCreature()) + { + summonedCreature->SetReactState(REACT_PASSIVE); + summonedCreature->setDeathState(DeathState::JustRespawned); + summonedCreature->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + } + }); } + } - void Reset() override + void SetRoomState(GOState state) + { + //TODO: handle door closing + if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_BRIDGE_WINDOW))) + window->SetGoState(state); + if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_KAEL_STATUE_RIGHT))) + window->SetGoState(state); + if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(GO_KAEL_STATUE_LEFT))) + window->SetGoState(state); + } + + void Reset() override + { + BossAI::Reset(); + scheduler.Schedule(1s, [this](TaskContext) { - BossAI::Reset(); - events2.Reset(); - events2.ScheduleEvent(EVENT_GATHER_ADVISORS, 1000); - phase = PHASE_NONE; + PrepareAdvisors(); + }); + _phase = PHASE_NONE; - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_HOVER, true); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - SetRoomState(GO_STATE_READY); + me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_HOVER, true); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + SetRoomState(GO_STATE_READY); + me->SetDisableGravity(false); + me->SetWalk(false); + ScheduleHealthCheckEvent(50, [&]{ + scheduler.CancelAll(); + me->CastStop(); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MovePoint(POINT_MIDDLE, me->GetHomePosition(), true, true); + me->ClearUnitState(UNIT_STATE_MELEE_ATTACKING); + me->SendMeleeAttackStop(); + }); + } + + void AttackStart(Unit* who) override + { + if (_phase == PHASE_FINAL /* check is scheduled&& events.GetNextEventTime(EVENT_GRAVITY_LAPSE_END) == 0*/) + BossAI::AttackStart(who); + } + + void MoveInLineOfSight(Unit* who) override + { + if (_phase == PHASE_NONE && who->GetTypeId() == TYPEID_PLAYER && me->IsValidAttackTarget(who)) + { + _phase = PHASE_SINGLE_ADVISOR; + me->SetInCombatWithZone(); + Talk(SAY_INTRO); + ScheduleUniqueTimedEvent(23s, [&] + { + Talk(SAY_INTRO_THALADRED); + }, EVENT_PREFIGHT_PHASE11); + ScheduleUniqueTimedEvent(30s, [&] + { + if (Creature* thaladred = summons.GetCreatureWithEntry(NPC_THALADRED)) + { + thaladred->SetReactState(REACT_AGGRESSIVE); + thaladred->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + thaladred->AI()->AttackStart(target); + thaladred->SetInCombatWithZone(); + } + }, EVENT_PREFIGHT_PHASE12); + } + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + if (summon->GetEntry() == NPC_NETHER_VAPOR) + summon->GetMotionMaster()->MoveRandom(20.0f); + } + + void DoAction(int32 action) override + { + switch(action) + { + case ACTION_START_SANGUINAR: + IntroduceNewAdvisor(SAY_INTRO_SANGUINAR, ACTION_START_SANGUINAR); + break; + case ACTION_START_CAPERNIAN: + IntroduceNewAdvisor(SAY_INTRO_CAPERNIAN, ACTION_START_CAPERNIAN); + break; + case ACTION_START_TELONICUS: + IntroduceNewAdvisor(SAY_INTRO_TELONICUS, ACTION_START_TELONICUS); + break; + case ACTION_START_WEAPONS: + ScheduleUniqueTimedEvent(3s, [&]{ + Talk(SAY_PHASE2_WEAPON); + DoCastSelf(SPELL_SUMMON_WEAPONS); + _phase = PHASE_WEAPONS; + }, EVENT_PREFIGHT_PHASE51); + ScheduleUniqueTimedEvent(9s, [&]{ + summons.DoForAllSummons([&](WorldObject* summon) + { + if (Creature* summonedCreature = summon->ToCreature()) + { + if (!summonedCreature->GetSpawnId()) + { + summonedCreature->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + summonedCreature->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + summonedCreature->AI()->AttackStart(target); + } + } + } + }); + ScheduleUniqueTimedEvent(2min, [&]{ + PhaseAllAdvisorsExecute(); + }, EVENT_PREFIGHT_PHASE61); + }, EVENT_PREFIGHT_PHASE52); + break; + default: + break; + } + } + + void MovementInform(uint32 type, uint32 point) override + { + if (type != POINT_MOTION_TYPE) + return; + + if (point == POINT_MIDDLE) + { + ExecuteMiddleEvent(); + } + else if (point == POINT_START_LAST_PHASE) + { me->SetDisableGravity(false); me->SetWalk(false); - } - - void AttackStart(Unit* who) override - { - if (phase == PHASE_FINAL && events.GetNextEventTime(EVENT_GRAVITY_LAPSE_END) == 0) - BossAI::AttackStart(who); - } - - void MoveInLineOfSight(Unit* who) override - { - if (phase == PHASE_NONE && who->GetTypeId() == TYPEID_PLAYER && me->IsValidAttackTarget(who)) + me->RemoveAurasDueToSpell(SPELL_KAEL_FULL_POWER); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + //re-set validator + scheduler.SetValidator([this]{ + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + ScheduleTimedEvent(0ms, [&] { - phase = PHASE_SINGLE_ADVISOR; - me->SetInCombatWithZone(); - Talk(SAY_INTRO); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE11, 23000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE12, 30000); - } - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_SLAY); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - if (summon->GetEntry() == NPC_NETHER_VAPOR) - summon->GetMotionMaster()->MoveRandom(20.0f); - } - - void SummonedCreatureDies(Creature* summon, Unit*) override - { - if (phase == PHASE_FINAL) - return; - - if (summon->GetSpawnId() && phase == PHASE_ALL_ADVISORS) + DoCastVictim(SPELL_FIREBALL); + }, 2400ms, 7500ms); + ScheduleTimedEvent(10000ms, [&] { - for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i) - if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) - if (summon->GetSpawnId() && summon->IsAlive()) - return; - - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE71, 2000); - return; - } - - if (summon->GetEntry() == NPC_THALADRED) + DoCastRandomTarget(SPELL_FLAME_STRIKE, 0, 100.0f); + }, 30250ms, 50650ms); + ScheduleTimedEvent(20000ms, [&] { - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE21, 2000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE22, 14500); - } - else if (summon->GetEntry() == NPC_LORD_SANGUINAR) + Talk(SAY_SUMMON_PHOENIX); + DoCastSelf(SPELL_PHOENIX); + }, 31450ms, 66550ms); + ScheduleTimedEvent(5s, [&] { - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE31, 2000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE32, 9000); - } - else if (summon->GetEntry() == NPC_CAPERNIAN) - { - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE41, 2000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE42, 10400); - } - else if (summon->GetEntry() == NPC_TELONICUS) - { - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE51, 3000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE52, 9000); - } - } - - void JustDied(Unit* killer) override - { - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - - Talk(SAY_DEATH); - BossAI::JustDied(killer); - } - - void MovementInform(uint32 type, uint32 point) override - { - if (type != POINT_MOTION_TYPE) - return; - - if (point == POINT_MIDDLE) - { - events2.ScheduleEvent(EVENT_SCENE_1, 0); - events2.ScheduleEvent(EVENT_SCENE_2, 2500); - events2.ScheduleEvent(EVENT_SCENE_3, 4000); - events2.ScheduleEvent(EVENT_SCENE_4, 7000); - events2.ScheduleEvent(EVENT_SCENE_5, 10000); - events2.ScheduleEvent(EVENT_SCENE_6, 14000); - events2.ScheduleEvent(EVENT_SCENE_7, 17500); - events2.ScheduleEvent(EVENT_SCENE_8, 19000); - events2.ScheduleEvent(EVENT_SCENE_9, 22000); // two first lightnings + aura - events2.ScheduleEvent(EVENT_SCENE_10, 22800); // two - events2.ScheduleEvent(EVENT_SCENE_11, 23600); // two - events2.ScheduleEvent(EVENT_SCENE_12, 24500); // two - events2.ScheduleEvent(EVENT_SCENE_13, 24800); // two - events2.ScheduleEvent(EVENT_SCENE_14, 25300); // two - events2.ScheduleEvent(EVENT_SCENE_15, 32000); // full power - events2.ScheduleEvent(EVENT_SCENE_16, 36000); // remove lightnings + aura, move down - } - else if (point == POINT_START_LAST_PHASE) - { - me->SetDisableGravity(false); - me->SetWalk(false); - me->RemoveAurasDueToSpell(SPELL_KAEL_FULL_POWER); - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - events.SetTimer(60000); - events.ScheduleEvent(EVENT_SPELL_FIREBALL, 0); - events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 10000); - events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 20000); - events.ScheduleEvent(EVENT_SPELL_GRAVITY_LAPSE, 5000); - if (me->GetVictim()) + scheduler.DelayAll(30s); + me->setAttackTimer(BASE_ATTACK, 30000); + DoCastSelf(SPELL_GRAVITY_LAPSE); + DoCastSelf(SPELL_SUMMON_NETHER_VAPOR); + scheduler.Schedule(4s, GROUP_NETHER_BEAM, [this](TaskContext context) + { + DoCastSelf(SPELL_NETHER_BEAM); + context.Repeat(4s); + }).Schedule(0s, GROUP_SHOCK_BARRIER, [this](TaskContext context) + { + DoCastSelf(SPELL_SHOCK_BARRIER); + context.Repeat(10s); + }).Schedule(20500ms, GROUP_SHOCK_BARRIER, [this](TaskContext) + { + scheduler.CancelGroup(GROUP_SHOCK_BARRIER); + }).Schedule(32s, [this](TaskContext) { - me->SetTarget(me->GetVictim()->GetGUID()); - AttackStart(me->GetVictim()); - } - } - } - - void UpdateAI(uint32 diff) override - { - events2.Update(diff); - switch (events2.ExecuteEvent()) - { - case EVENT_GATHER_ADVISORS: - PrepareAdvisors(); - break; - case EVENT_PREFIGHT_PHASE11: - Talk(SAY_INTRO_THALADRED); - break; - case EVENT_PREFIGHT_PHASE12: - if (Creature* advisor = summons.GetCreatureWithEntry(NPC_THALADRED)) - { - advisor->SetReactState(REACT_AGGRESSIVE); - advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - advisor->AI()->AttackStart(target); - advisor->SetInCombatWithZone(); - advisor->AI()->Talk(SAY_THALADRED_AGGRO); - } - break; - case EVENT_PREFIGHT_PHASE21: - Talk(SAY_INTRO_SANGUINAR); - break; - case EVENT_PREFIGHT_PHASE22: - if (Creature* advisor = summons.GetCreatureWithEntry(NPC_LORD_SANGUINAR)) - { - advisor->SetReactState(REACT_AGGRESSIVE); - advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - advisor->AI()->AttackStart(target); - advisor->SetInCombatWithZone(); - advisor->AI()->Talk(SAY_SANGUINAR_AGGRO); - } - break; - case EVENT_PREFIGHT_PHASE31: - Talk(SAY_INTRO_CAPERNIAN); - break; - case EVENT_PREFIGHT_PHASE32: - if (Creature* advisor = summons.GetCreatureWithEntry(NPC_CAPERNIAN)) - { - advisor->SetReactState(REACT_AGGRESSIVE); - advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - advisor->AI()->AttackStart(target); - advisor->SetInCombatWithZone(); - advisor->AI()->Talk(SAY_CAPERNIAN_AGGRO); - } - break; - case EVENT_PREFIGHT_PHASE41: - Talk(SAY_INTRO_TELONICUS); - break; - case EVENT_PREFIGHT_PHASE42: - if (Creature* advisor = summons.GetCreatureWithEntry(NPC_TELONICUS)) - { - advisor->SetReactState(REACT_AGGRESSIVE); - advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - advisor->AI()->AttackStart(target); - advisor->SetInCombatWithZone(); - advisor->AI()->Talk(SAY_TELONICUS_AGGRO); - } - break; - case EVENT_PREFIGHT_PHASE51: - Talk(SAY_PHASE2_WEAPON); - me->CastSpell(me, SPELL_SUMMON_WEAPONS, false); - phase = PHASE_WEAPONS; - break; - case EVENT_PREFIGHT_PHASE52: - for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i) - { - if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) - if (!summon->GetSpawnId()) - { - summon->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); - summon->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - summon->AI()->AttackStart(target); - } - } - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE61, 2 * MINUTE * IN_MILLISECONDS); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE62, 2 * MINUTE * IN_MILLISECONDS + 6000); - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE63, 2 * MINUTE * IN_MILLISECONDS + 12000); - break; - case EVENT_PREFIGHT_PHASE61: - phase = PHASE_ALL_ADVISORS; - Talk(SAY_PHASE3_ADVANCE); - break; - case EVENT_PREFIGHT_PHASE62: - me->CastSpell(me, SPELL_RESURRECTION, false); - break; - case EVENT_PREFIGHT_PHASE63: - for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i) - if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) - if (summon->GetSpawnId()) - { - summon->SetReactState(REACT_AGGRESSIVE); - summon->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); - summon->SetInCombatWithZone(); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - summon->AI()->AttackStart(target); - } - events2.ScheduleEvent(EVENT_PREFIGHT_PHASE71, 3 * MINUTE * IN_MILLISECONDS); - break; - case EVENT_PREFIGHT_PHASE71: - events2.CancelEvent(EVENT_PREFIGHT_PHASE71); - Talk(SAY_PHASE4_INTRO2); - phase = PHASE_FINAL; - DoResetThreatList(); - me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) - AttackStart(target); - - events2.Reset(); - events.Reset(); - events.ScheduleEvent(EVENT_SPELL_FIREBALL, 1000); - events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 15000); - events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 30000); - events.ScheduleEvent(EVENT_SPELL_SEQ_1, 20000); - events.ScheduleEvent(EVENT_SPELL_SEQ_2, 40000); - events.ScheduleEvent(EVENT_SPELL_SEQ_3, 60000); - events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000); - break; - case EVENT_SCENE_1: - me->SetTarget(); - me->SetFacingTo(M_PI); - me->SetWalk(true); - Talk(SAY_PHASE5_NUTS); - break; - case EVENT_SCENE_2: - me->SetTarget(); - me->CastSpell(me, SPELL_KAEL_EXPLODES1, true); - me->CastSpell(me, SPELL_KAEL_GAINING_POWER, false); - me->SetDisableGravity(true); - break; - case EVENT_SCENE_3: - me->SetTarget(); - for (uint8 i = 0; i < 2; ++i) - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i], TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); - me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), 76.0f, false, true); - me->CastSpell(me, SPELL_GROW, true); - break; - case EVENT_SCENE_4: - me->SetTarget(); - me->CastSpell(me, SPELL_GROW, true); - me->CastSpell(me, SPELL_KAEL_EXPLODES2, true); - me->CastSpell(me, SPELL_NETHERBEAM_AURA1, true); - for (uint8 i = 0; i < 2; ++i) - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i + 2], TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); - break; - case EVENT_SCENE_5: - me->SetTarget(); - me->CastSpell(me, SPELL_GROW, true); - me->CastSpell(me, SPELL_KAEL_EXPLODES3, true); - me->CastSpell(me, SPELL_NETHERBEAM_AURA2, true); - for (uint8 i = 0; i < 2; ++i) - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i + 4], TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); - break; - case EVENT_SCENE_6: - me->CastSpell(me, SPELL_GROW, true); - me->CastSpell(me, SPELL_KAEL_EXPLODES4, true); - me->CastSpell(me, SPELL_NETHERBEAM_AURA3, true); - break; - case EVENT_SCENE_7: - SetRoomState(GO_STATE_ACTIVE); - me->SetUnitMovementFlags(MOVEMENTFLAG_HOVER | MOVEMENTFLAG_WALKING | MOVEMENTFLAG_DISABLE_GRAVITY); - me->SendMovementFlagUpdate(); - break; - case EVENT_SCENE_8: - summons.DespawnEntry(WORLD_TRIGGER); - me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA1); - me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA2); - me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA3); - me->CastSpell(me, SPELL_KAEL_EXPLODES5, true); - me->CastSpell(me, SPELL_FLOATING_DROWNED, false); - //me->CastSpell(me, SPELL_KEAL_STUNNED, true); - break; - case EVENT_SCENE_9: - me->CastSpell(me, 52241, true); // WRONG VISUAL - me->CastSpell(me, 34807, true); - me->SummonCreature(NPC_WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000); - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() + 5, me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true); - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() - 5, me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); - break; - case EVENT_SCENE_10: - 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); - 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_BEAM1, true); - break; - case EVENT_SCENE_11: - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY() + 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); - break; - case EVENT_SCENE_12: - if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY() - 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) - trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true); - 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_BEAM1, true); - break; - case EVENT_SCENE_13: - 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_BEAM2, true); - break; - case EVENT_SCENE_14: - //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); - break; - case EVENT_SCENE_15: - me->RemoveAurasDueToSpell(SPELL_FLOATING_DROWNED); - me->RemoveAurasDueToSpell(SPELL_KEAL_STUNNED); - me->CastSpell(me, SPELL_KAEL_FULL_POWER, false); - me->CastSpell(me, 36709, true); - me->CastSpell(me, 36201, true); - me->CastSpell(me, 36290, true); - me->CastSpell(me, 36291, true); - me->SetUnitMovementFlags(MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_WALKING); - me->SendMovementFlagUpdate(); - break; - case EVENT_SCENE_16: - summons.DespawnEntry(WORLD_TRIGGER); - me->RemoveAurasDueToSpell(52241); // WRONG VISUAL - me->GetMotionMaster()->MovePoint(POINT_START_LAST_PHASE, me->GetHomePosition(), false, true); - break; - } - - if (!events2.Empty()) - return; - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SPELL_SEQ_1: - events.ScheduleEvent(EVENT_SPELL_MIND_CONTROL, 0); - events.ScheduleEvent(EVENT_SPELL_ARCANE_DISRUPTION, 3000); - events.ScheduleEvent(EVENT_SPELL_SEQ_1, 50000); - break; - case EVENT_SPELL_SEQ_2: - events.ScheduleEvent(EVENT_SPELL_MIND_CONTROL, 3000); - events.ScheduleEvent(EVENT_SPELL_ARCANE_DISRUPTION, 6000); - events.ScheduleEvent(EVENT_SPELL_SEQ_2, 50000); - break; - case EVENT_SPELL_SEQ_3: - Talk(SAY_PYROBLAST); - me->CastSpell(me, SPELL_SHOCK_BARRIER, false); - events.ScheduleEvent(EVENT_SPELL_SEQ_3, 50000); - events.DelayEvents(10000); - events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 0); - events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 4000); - events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 8000); - break; - case EVENT_SPELL_SHOCK_BARRIER: - me->CastSpell(me, SPELL_SHOCK_BARRIER, false); - break; - case EVENT_SPELL_FIREBALL: - me->CastSpell(me->GetVictim(), SPELL_FIREBALL, false); - events.ScheduleEvent(EVENT_SPELL_FIREBALL, roll_chance_i(70) ? 2000 : 4000); - break; - case EVENT_SPELL_PYROBLAST: - me->CastSpell(me->GetVictim(), SPELL_PYROBLAST, false); - break; - case EVENT_SPELL_FLAMESTRIKE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) - me->CastSpell(target, SPELL_FLAME_STRIKE, false); - events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 20000); - break; - case EVENT_SPELL_ARCANE_DISRUPTION: - me->CastSpell(me, SPELL_ARCANE_DISRUPTION, false); - break; - case EVENT_SPELL_MIND_CONTROL: - if (roll_chance_i(50)) - Talk(SAY_MINDCONTROL); - me->CastCustomSpell(SPELL_MIND_CONTROL, SPELLVALUE_MAX_TARGETS, 3, me, false); - break; - case EVENT_SPELL_SUMMON_PHOENIX: - Talk(SAY_SUMMON_PHOENIX); - me->CastSpell(me, SPELL_PHOENIX, false); - events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 40000); - break; - case EVENT_CHECK_HEALTH: - if (me->HealthBelowPct(51)) - { - events.Reset(); - me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - me->SetReactState(REACT_PASSIVE); - me->GetMotionMaster()->MovePoint(POINT_MIDDLE, me->GetHomePosition(), true, true); - me->ClearUnitState(UNIT_STATE_MELEE_ATTACKING); - me->SendMeleeAttackStop(); - break; - } - events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000); - break; - case EVENT_SPELL_GRAVITY_LAPSE: - events.DelayEvents(30000); - me->setAttackTimer(BASE_ATTACK, 30000); - events.ScheduleEvent(EVENT_SPELL_GRAVITY_LAPSE, 90000); - events.ScheduleEvent(EVENT_GRAVITY_LAPSE_END, 32000); - events.ScheduleEvent(EVENT_SPELL_SHOCK_BARRIER, 20000); - events.ScheduleEvent(EVENT_SPELL_SHOCK_BARRIER, 10000); - events.ScheduleEvent(EVENT_SPELL_NETHER_BEAM, 4000); - events.ScheduleEvent(EVENT_SPELL_NETHER_VAPOR, 0); - me->CastSpell(me, SPELL_SHOCK_BARRIER, false); - me->CastSpell(me, SPELL_GRAVITY_LAPSE, false); - me->SetTarget(); - me->GetMotionMaster()->Clear(); - me->StopMoving(); - Talk(SAY_GRAVITYLAPSE); - break; - case EVENT_SPELL_NETHER_VAPOR: - me->CastSpell(me, SPELL_SUMMON_NETHER_VAPOR, false); - break; - case EVENT_SPELL_NETHER_BEAM: - me->CastSpell(me, SPELL_NETHER_BEAM, false); - events.ScheduleEvent(EVENT_SPELL_NETHER_BEAM, 4000); - break; - case EVENT_GRAVITY_LAPSE_END: summons.DespawnEntry(NPC_NETHER_VAPOR); - events.CancelEvent(EVENT_SPELL_NETHER_BEAM); + scheduler.CancelGroup(GROUP_NETHER_BEAM); me->SetTarget(me->GetVictim()->GetGUID()); me->GetMotionMaster()->MoveChase(me->GetVictim()); - break; + }); + me->SetTarget(); + me->GetMotionMaster()->Clear(); + me->StopMoving(); + Talk(SAY_GRAVITYLAPSE); + }, 90s); + if (me->GetVictim()) + { + me->SetTarget(me->GetVictim()->GetGUID()); + AttackStart(me->GetVictim()); } - - DoMeleeAttackIfReady(); } - - bool CheckEvadeIfOutOfCombatArea() const override - { - return me->GetHomePosition().GetExactDist2d(me) > 165.0f || !SelectTargetFromPlayerList(165.0f); - } - }; - CreatureAI* GetAI(Creature* creature) const override - { - return GetTheEyeAI(creature); } -}; + void ExecuteMiddleEvent() + { + scheduler.ClearValidator(); + me->SetTarget(); + me->SetFacingTo(M_PI); + me->SetWalk(true); + Talk(SAY_PHASE5_NUTS); + ScheduleUniqueTimedEvent(2500ms, [&] + { + me->SetTarget(); + DoCastSelf(SPELL_KAEL_EXPLODES1, true); + DoCastSelf(SPELL_KAEL_GAINING_POWER); + me->SetDisableGravity(true); + }, EVENT_SCENE_2); + ScheduleUniqueTimedEvent(4000ms, [&] + { + me->SetTarget(); + for (uint8 i = 0; i < 2; ++i) + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i], TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); + me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), 76.0f, false, true); + DoCastSelf(SPELL_GROW, true); + }, EVENT_SCENE_3); + ScheduleUniqueTimedEvent(7000ms, [&] + { + me->SetTarget(); + DoCastSelf(SPELL_GROW, true); + DoCastSelf(SPELL_KAEL_EXPLODES2, true); + DoCastSelf(SPELL_NETHERBEAM_AURA1, true); + for (uint8 i = 0; i < 2; ++i) + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i + 2], TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); + }, EVENT_SCENE_4); + ScheduleUniqueTimedEvent(10000ms, [&] + { + me->SetTarget(); + DoCastSelf(SPELL_GROW, true); + DoCastSelf(SPELL_KAEL_EXPLODES3, true); + DoCastSelf(SPELL_NETHERBEAM_AURA2, true); + for (uint8 i = 0; i < 2; ++i) + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i + 4], TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_NETHERBEAM1 + i, false); + }, EVENT_SCENE_5); + ScheduleUniqueTimedEvent(14000ms, [&] + { + DoCastSelf(SPELL_GROW, true); + DoCastSelf(SPELL_KAEL_EXPLODES4, true); + DoCastSelf(SPELL_NETHERBEAM_AURA3, true); + }, EVENT_SCENE_6); + ScheduleUniqueTimedEvent(17500ms, [&] + { + SetRoomState(GO_STATE_ACTIVE); + me->SetUnitMovementFlags(MOVEMENTFLAG_HOVER | MOVEMENTFLAG_WALKING | MOVEMENTFLAG_DISABLE_GRAVITY); + me->SendMovementFlagUpdate(); + }, EVENT_SCENE_7); + ScheduleUniqueTimedEvent(19000ms, [&] + { + summons.DespawnEntry(WORLD_TRIGGER); + me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA1); + 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, [&] + { + DoCastSelf(SPELL_DARK_BANISH_STATE, true); + DoCastSelf(SPELL_ARCANE_EXPLOSION_VISUAL, true); + me->SummonCreature(NPC_WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000); + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() + 5, me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true); + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX() - 5, me->GetPositionY(), me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); + }, EVENT_SCENE_9); + ScheduleUniqueTimedEvent(22800ms, [&] + { + 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); + 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_BEAM1, true); + }, EVENT_SCENE_10); + ScheduleUniqueTimedEvent(23600ms, [&] + { + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY() + 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true); + }, EVENT_SCENE_11); + ScheduleUniqueTimedEvent(24500ms, [&] + { + if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY() - 5, me->GetPositionZ() + 15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000)) + trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true); + 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_BEAM1, true); + }, EVENT_SCENE_12); + ScheduleUniqueTimedEvent(24800ms, [&] + { + 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_BEAM2, true); + }, EVENT_SCENE_13); + ScheduleUniqueTimedEvent(25300ms, [&] + { + 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(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); + me->SetUnitMovementFlags(MOVEMENTFLAG_DISABLE_GRAVITY | MOVEMENTFLAG_WALKING); + me->SendMovementFlagUpdate(); + }, EVENT_SCENE_15); + ScheduleUniqueTimedEvent(36000ms, [&] + { + summons.DespawnEntry(WORLD_TRIGGER); + me->CastStop(); + me->GetMotionMaster()->Clear(); + me->RemoveAurasDueToSpell(SPELL_DARK_BANISH_STATE); // WRONG VISUAL + me->GetMotionMaster()->MovePoint(POINT_START_LAST_PHASE, me->GetHomePosition(), false, true); + }, EVENT_SCENE_16); + } + + void IntroduceNewAdvisor(Yells talkIntroduction, KaelActions kaelAction) + { + std::chrono::milliseconds attackStartTimer = 0ms; + EyeNPCs advisorNPCId = NPC_THALADRED; + scheduler.Schedule(2s, [this, talkIntroduction](TaskContext) + { + Talk(talkIntroduction); + }); + //switch because talk times are different + switch(kaelAction) + { + case ACTION_START_SANGUINAR: + attackStartTimer = 14500ms; + advisorNPCId = NPC_LORD_SANGUINAR; + break; + case ACTION_START_CAPERNIAN: + attackStartTimer = 9000ms; + advisorNPCId = NPC_CAPERNIAN; + break; + case ACTION_START_TELONICUS: + attackStartTimer = 10400ms; + advisorNPCId = NPC_TELONICUS; + break; + default: + break; + } + scheduler.Schedule(attackStartTimer, [this, advisorNPCId](TaskContext) + { + if (Creature* advisor = summons.GetCreatureWithEntry(advisorNPCId)) + { + advisor->SetReactState(REACT_AGGRESSIVE); + advisor->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + advisor->AI()->AttackStart(target); + advisor->SetInCombatWithZone(); + } + }); + } + + void PhaseAllAdvisorsExecute() + { + //remove all weapons so they don't get revived + summons.DoForAllSummons([&](WorldObject* summon) + { + if (Creature* summonedCreature = summon->ToCreature()) + { + if (summonedCreature->GetEntry() >= 21268 && summonedCreature->GetEntry() <= 21274) + { + summonedCreature->DespawnOrUnsummon(); + } + } + }); + _phase = PHASE_ALL_ADVISORS; + Talk(SAY_PHASE3_ADVANCE); + ScheduleUniqueTimedEvent(6s, [&]{ + DoCastSelf(SPELL_RESURRECTION); + }, EVENT_PREFIGHT_PHASE62); + ScheduleUniqueTimedEvent(12s, [&]{ + summons.DoForAllSummons([&](WorldObject* summon) + { + if (Creature* summonedCreature = summon->ToCreature()) + { + if (summonedCreature->GetSpawnId()) + { + summonedCreature->SetReactState(REACT_AGGRESSIVE); + summonedCreature->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + summonedCreature->SetInCombatWithZone(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + summonedCreature->AI()->AttackStart(target); + } + } + } + }); + ScheduleUniqueTimedEvent(3min, [&]{ + PhaseKaelExecute(); + }, EVENT_PREFIGHT_PHASE71); + }, EVENT_PREFIGHT_PHASE63); + } + + void PhaseKaelExecute() + { + scheduler.CancelAll(); + Talk(SAY_PHASE4_INTRO2); + _phase = PHASE_FINAL; + DoResetThreatList(); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) + { + AttackStart(target); + } + ScheduleTimedEvent(1000ms, [&] + { + DoCastVictim(SPELL_FIREBALL); + }, 2400ms, 7500ms); + ScheduleTimedEvent(15000ms, [&] + { + DoCastRandomTarget(SPELL_FLAME_STRIKE, 0, 100.0f); + }, 30250ms, 50650ms); + ScheduleTimedEvent(30000ms, [&] + { + Talk(SAY_SUMMON_PHOENIX); + DoCastSelf(SPELL_PHOENIX); + }, 31450ms, 66550ms); + //sequence + ScheduleTimedEvent(20s, [&] + { + if (roll_chance_i(50)) + Talk(SAY_MINDCONTROL); + me->CastCustomSpell(SPELL_MIND_CONTROL, SPELLVALUE_MAX_TARGETS, 3, me, false); + scheduler.Schedule(3s, [this](TaskContext) + { + DoCastSelf(SPELL_ARCANE_DISRUPTION); + }); + }, 50s); + ScheduleTimedEvent(40s, [&] + { + scheduler.Schedule(3s, [this](TaskContext) + { + if (roll_chance_i(50)) + Talk(SAY_MINDCONTROL); + me->CastCustomSpell(SPELL_MIND_CONTROL, SPELLVALUE_MAX_TARGETS, 3, me, false); + }).Schedule(6s, [this](TaskContext) + { + DoCastSelf(SPELL_ARCANE_DISRUPTION); + }); + }, 50s); + ScheduleTimedEvent(60s, [&] + { + Talk(SAY_PYROBLAST); + DoCastSelf(SPELL_SHOCK_BARRIER); + scheduler.DelayAll(10s); + scheduler.Schedule(0s, GROUP_PYROBLAST, [this](TaskContext context) + { + DoCastVictim(SPELL_PYROBLAST); + context.Repeat(4s); + }).Schedule(8500ms, GROUP_PYROBLAST, [this](TaskContext) + { + scheduler.CancelGroup(GROUP_PYROBLAST); + }); + }, 50s); + } + + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + + bool CheckEvadeIfOutOfCombatArea() const override + { + return me->GetHomePosition().GetExactDist2d(me) > 165.0f || !SelectTargetFromPlayerList(165.0f); + } +private: + uint32 _phase; +}; struct npc_lord_sanguinar : public ScriptedAI { npc_lord_sanguinar(Creature* creature) : ScriptedAI(creature) { + _instance = creature->GetInstanceScript(); scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); @@ -759,10 +761,16 @@ struct npc_lord_sanguinar : public ScriptedAI void Reset() override { scheduler.CancelAll(); + _hasDied = false; + me->SetReactState(REACT_PASSIVE); } void JustEngagedWith(Unit* /*who*/) override { + if (!_hasDied) + { + Talk(SAY_SANGUINAR_AGGRO); + } ScheduleTimedEvent(0s, [&]{ DoCastSelf(SPELL_BELLOWING_ROAR); }, 15s); @@ -770,8 +778,16 @@ struct npc_lord_sanguinar : public ScriptedAI void JustDied(Unit* /*killer*/) override { - Talk(SAY_SANGUINAR_DEATH); - DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (!_hasDied) + { + Talk(SAY_SANGUINAR_DEATH); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (Creature* kael = _instance->GetCreature(DATA_KAELTHAS)) + { + kael->AI()->DoAction(ACTION_START_CAPERNIAN); + } + _hasDied = true; + } } void UpdateAI(uint32 diff) override @@ -786,11 +802,15 @@ struct npc_lord_sanguinar : public ScriptedAI DoMeleeAttackIfReady(); } +private: + InstanceScript* _instance; + bool _hasDied; }; struct npc_capernian : public ScriptedAI { npc_capernian(Creature* creature) : ScriptedAI(creature) { + _instance = creature->GetInstanceScript(); scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); @@ -800,10 +820,16 @@ struct npc_capernian : public ScriptedAI void Reset() override { scheduler.CancelAll(); + _hasDied = false; + me->SetReactState(REACT_PASSIVE); } void JustEngagedWith(Unit* /*who*/) override { + if (!_hasDied) + { + Talk(SAY_CAPERNIAN_AGGRO); + } ScheduleTimedEvent(0ms, [&]{ DoCastVictim(SPELL_CAPERNIAN_FIREBALL); }, 2500ms); @@ -817,8 +843,16 @@ struct npc_capernian : public ScriptedAI void JustDied(Unit* /*killer*/) override { - Talk(SAY_CAPERNIAN_DEATH); - DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (!_hasDied) + { + Talk(SAY_CAPERNIAN_DEATH); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (Creature* kael = _instance->GetCreature(DATA_KAELTHAS)) + { + kael->AI()->DoAction(ACTION_START_TELONICUS); + } + _hasDied = true; + } } void UpdateAI(uint32 diff) override @@ -833,11 +867,15 @@ struct npc_capernian : public ScriptedAI DoMeleeAttackIfReady(); } +private: + InstanceScript* _instance; + bool _hasDied; }; struct npc_telonicus : public ScriptedAI { npc_telonicus(Creature* creature) : ScriptedAI(creature) { + _instance = creature->GetInstanceScript(); scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); @@ -847,10 +885,16 @@ struct npc_telonicus : public ScriptedAI void Reset() override { scheduler.CancelAll(); + _hasDied = false; + me->SetReactState(REACT_PASSIVE); } void JustEngagedWith(Unit* /*who*/) override { + if (!_hasDied) + { + Talk(SAY_TELONICUS_AGGRO); + } ScheduleTimedEvent(0ms, [&]{ DoCastVictim(SPELL_BOMB); }, 3600ms, 7100ms); @@ -861,8 +905,16 @@ struct npc_telonicus : public ScriptedAI void JustDied(Unit* /*killer*/) override { - Talk(SAY_TELONICUS_DEATH); - DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (!_hasDied) + { + Talk(SAY_TELONICUS_DEATH); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (Creature* kael = _instance->GetCreature(DATA_KAELTHAS)) + { + kael->AI()->DoAction(ACTION_START_WEAPONS); + } + _hasDied = true; + } } void UpdateAI(uint32 diff) override @@ -877,11 +929,15 @@ struct npc_telonicus : public ScriptedAI DoMeleeAttackIfReady(); } +private: + InstanceScript* _instance; + bool _hasDied; }; struct npc_thaladred : public ScriptedAI { npc_thaladred(Creature* creature) : ScriptedAI(creature) { + _instance = creature->GetInstanceScript(); scheduler.SetValidator([this] { return !me->HasUnitState(UNIT_STATE_CASTING); @@ -891,11 +947,17 @@ struct npc_thaladred : public ScriptedAI void Reset() override { scheduler.CancelAll(); + me->SetReactState(REACT_PASSIVE); + _hasDied = false; me->SetWalk(true); } void JustEngagedWith(Unit* /*who*/) override { + if (!_hasDied) + { + Talk(SAY_THALADRED_AGGRO); + } ScheduleTimedEvent(100ms, [&] { DoResetThreatList(); @@ -927,8 +989,16 @@ struct npc_thaladred : public ScriptedAI void JustDied(Unit* /*killer*/) override { - Talk(SAY_THALADRED_DEATH); - DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (!_hasDied) + { + Talk(SAY_THALADRED_DEATH); + DoCastSelf(SPELL_KAEL_PHASE_TWO, true); + if (Creature* kael = _instance->GetCreature(DATA_KAELTHAS)) + { + kael->AI()->DoAction(ACTION_START_SANGUINAR); + } + _hasDied = true; + } } void UpdateAI(uint32 diff) override @@ -943,6 +1013,9 @@ struct npc_thaladred : public ScriptedAI DoMeleeAttackIfReady(); } +private: + InstanceScript* _instance; + bool _hasDied; }; class spell_kaelthas_kael_phase_two : public SpellScriptLoader @@ -1275,7 +1348,7 @@ public: void AddSC_boss_kaelthas() { - new boss_kaelthas(); + RegisterTheEyeAI(boss_kaelthas); RegisterTheEyeAI(npc_lord_sanguinar); RegisterTheEyeAI(npc_capernian); RegisterTheEyeAI(npc_telonicus); diff --git a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp index 8b1ce688b..c02375e37 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/instance_the_eye.cpp @@ -20,6 +20,21 @@ #include "SpellScriptLoader.h" #include "the_eye.h" +ObjectData const creatureData[] = +{ + { NPC_KAELTHAS, DATA_KAELTHAS }, + { NPC_THALADRED, DATA_THALADRED }, + { NPC_LORD_SANGUINAR, DATA_LORD_SANGUINAR }, + { NPC_CAPERNIAN, DATA_CAPERNIAN }, + { NPC_TELONICUS, DATA_TELONICUS }, + { 0, 0 } +}; + +ObjectData const gameObjectData[] = +{ + { 0, 0 } +}; + class instance_the_eye : public InstanceMapScript { public: @@ -30,6 +45,7 @@ public: instance_the_eye_InstanceMapScript(Map* map) : InstanceScript(map) { SetHeaders(DataHeader); + LoadObjectData(creatureData, gameObjectData); SetBossNumber(MAX_ENCOUNTER); } @@ -66,6 +82,7 @@ public: LordSanguinarGUID = creature->GetGUID(); break; } + InstanceScript::OnCreatureCreate(creature); } void OnGameObjectCreate(GameObject* gobject) override @@ -98,14 +115,6 @@ public: return AlarGUID; case NPC_KAELTHAS: return KaelthasGUID; - case DATA_KAEL_ADVISOR1: - return ThaladredTheDarkenerGUID; - case DATA_KAEL_ADVISOR2: - return LordSanguinarGUID; - case DATA_KAEL_ADVISOR3: - return GrandAstromancerCapernianGUID; - case DATA_KAEL_ADVISOR4: - return MasterEngineerTelonicusGUID; } return ObjectGuid::Empty; diff --git a/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h b/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h index 5961fcc2d..f66fceb71 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h +++ b/src/server/scripts/Outland/TempestKeep/Eye/the_eye.h @@ -36,10 +36,10 @@ enum EyeData DATA_KAELTHAS = 3, MAX_ENCOUNTER = 4, - DATA_KAEL_ADVISOR1 = 10, - DATA_KAEL_ADVISOR2 = 11, - DATA_KAEL_ADVISOR3 = 12, - DATA_KAEL_ADVISOR4 = 13 + DATA_THALADRED = 10, + DATA_LORD_SANGUINAR = 11, + DATA_CAPERNIAN = 12, + DATA_TELONICUS = 13 }; enum EyeNPCs From bc25ade49825501aaee80b9da79ce14b1e7839cc Mon Sep 17 00:00:00 2001 From: Dan <83884799+elthehablo@users.noreply.github.com> Date: Thu, 8 Feb 2024 03:43:19 +0100 Subject: [PATCH 04/17] fix(Scripts/TheEye): refactor Al'ar (#18264) * initial * restore quill event * with ismoving and debug * final before timers * clear all debuffs * remove justreachedhome bossai * huh? * some fixes * more fixes * add 2 extra platforms for p1 * fix coords * finalise * change melt armor timer so it is not delayed indefinitely - this is because we have a dive after 30 secs that delays all timers. So at 30 secs we will have melt armor at 33 seconds, which then gets an extra 15 seconds from the delay all and another dive after 30 so it gets pushed back even more before being able to be cast --- .../spell_immunity_alar_and_adds.sql | 2 + .../Outland/TempestKeep/Eye/boss_alar.cpp | 581 +++++++++--------- 2 files changed, 298 insertions(+), 285 deletions(-) create mode 100644 data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql diff --git a/data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql b/data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql new file mode 100644 index 000000000..4143a0738 --- /dev/null +++ b/data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql @@ -0,0 +1,2 @@ +-- +UPDATE `creature_template` SET `spell_school_immune_mask` = `spell_school_immune_mask`|4 WHERE `entry` IN (19514, 19551); diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index 7264a07ae..e8e82b443 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -28,6 +28,7 @@ enum Spells SPELL_FLAME_QUILLS = 34229, SPELL_QUILL_MISSILE_1 = 34269, // 21 SPELL_QUILL_MISSILE_2 = 34314, // 3 + SPELL_CLEAR_ALL_DEBUFFS = 34098, SPELL_FLAME_BUFFET = 34121, SPELL_EMBER_BLAST = 34341, SPELL_REBIRTH_PHASE2 = 34342, @@ -38,15 +39,18 @@ enum Spells SPELL_DIVE_BOMB = 35181 }; -const Position alarPoints[7] = +// @todo: Alar doesnt seem to move to waypoints but instead to the triggers in p1 +const Position alarPoints[9] = { - {340.15f, 58.65f, 17.71f, 4.60f}, - {388.09f, 31.54f, 20.18f, 1.61f}, - {388.18f, -32.85f, 20.18f, 0.52f}, - {340.29f, -60.19f, 17.72f, 5.71f}, - {332.0f, 0.01f, 43.0f, 0.0f}, - {331.0f, 0.01f, -2.38f, 0.0f}, - {332.0f, 0.01f, 43.0f, 0.0f} + {335.638f, 59.4879f, 17.9319f, 4.60f}, //first platform + {388.751007f, 31.731199f, 20.263599f, 1.61f}, + {388.790985f, -33.105900f, 20.263599f, 0.52f}, + {332.722992f, -61.159f, 17.979099f, 5.71f}, + {258.959015f, -38.687099f, 20.262899f, 5.21f}, + {259.2277997, 35.879002f, 20.263f, 4.81f}, //sixth platform + {332.0f, 0.01f, 43.0f, 0.0f}, //quill + {331.0f, 0.01f, -2.38f, 0.0f}, //middle (p2) + {332.0f, 0.01f, 43.0f, 0.0f} // dive }; enum Misc @@ -56,26 +60,28 @@ enum Misc NPC_FLAME_PATCH = 20602, POINT_PLATFORM = 0, - POINT_QUILL = 4, - POINT_MIDDLE = 5, - POINT_DIVE = 6, + POINT_QUILL = 6, + POINT_MIDDLE = 7, + POINT_DIVE = 8, - EVENT_SWITCH_PLATFORM = 1, - EVENT_START_QUILLS = 2, - EVENT_RELOCATE_MIDDLE = 3, - EVENT_REBIRTH = 4, - EVENT_SPELL_MELT_ARMOR = 5, - EVENT_SPELL_FLAME_PATCH = 6, - EVENT_SPELL_CHARGE = 7, - EVENT_SPELL_DIVE_BOMB = 8, - EVENT_START_DIVE = 9, - EVENT_CAST_DIVE_BOMB = 10, - EVENT_SUMMON_DIVE_PHOENIX = 11, - EVENT_REBIRTH_DIVE = 12, - EVENT_SPELL_BERSERK = 13, + EVENT_RELOCATE_MIDDLE = 1, + EVENT_REBIRTH = 2, + EVENT_SPELL_BERSERK = 3, - EVENT_MOVE_TO_PHASE_2 = 20, - EVENT_FINISH_DIVE = 21 + EVENT_MOVE_TO_PHASE_2 = 4, + EVENT_FINISH_DIVE = 5 +}; + +enum PlatformMoveDirections +{ + DIRECTION_ANTI_CLOCKWISE = 0, + DIRECTION_CLOCKWISE = 1, + DIRECTION_ACROSS = 2 +}; + +enum GroupAlar +{ + GROUP_FLAME_BUFFET = 1 }; // Xinef: Ruse of the Ashtongue (10946) @@ -85,279 +91,284 @@ enum qruseoftheAshtongue QUEST_RUSE_OF_THE_ASHTONGUE = 10946, }; -class boss_alar : public CreatureScript +struct boss_alar : public BossAI { -public: - boss_alar() : CreatureScript("boss_alar") { } - struct boss_alarAI : public BossAI + boss_alar(Creature* creature) : BossAI(creature, DATA_ALAR) { - boss_alarAI(Creature* creature) : BossAI(creature, DATA_ALAR) - { - startPath = true; - SetCombatMovement(false); - } - - uint8 platform; - uint8 noQuillTimes; - bool startPath; - - void JustReachedHome() override - { - BossAI::JustReachedHome(); - startPath = true; - } - - void Reset() override - { - BossAI::Reset(); - platform = 0; - noQuillTimes = 0; - me->SetModelVisible(true); - me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FIRE, true); - me->SetReactState(REACT_AGGRESSIVE); - } - - void JustEngagedWith(Unit* who) override - { - BossAI::JustEngagedWith(who); - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 0); - } - - void JustDied(Unit* killer) override - { - me->SetModelVisible(true); - BossAI::JustDied(killer); - - // Xinef: Ruse of the Ashtongue (10946) - Map::PlayerList const& pl = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - { - Player* player = itr->GetSource(); - if (player->GetQuestStatus(QUEST_RUSE_OF_THE_ASHTONGUE) == QUEST_STATUS_INCOMPLETE) - if (player->HasAura(SPELL_ASHTONGUE_RUSE)) - player->AreaExploredOrEventHappens(QUEST_RUSE_OF_THE_ASHTONGUE); - } - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - if (summon->GetEntry() == NPC_EMBER_OF_ALAR) - summon->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FIRE, true); - } - - void MoveInLineOfSight(Unit* /*who*/) override { } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth() && platform < POINT_MIDDLE) - { - damage = 0; - if (events.GetNextEventTime(EVENT_REBIRTH) == 0) - { - me->InterruptNonMeleeSpells(false); - me->SetHealth(me->GetMaxHealth()); - me->SetReactState(REACT_PASSIVE); - me->CastSpell(me, SPELL_EMBER_BLAST, true); - - me->setAttackTimer(BASE_ATTACK, 16000); - events.Reset(); - events.ScheduleEvent(EVENT_RELOCATE_MIDDLE, 8000); - events.ScheduleEvent(EVENT_MOVE_TO_PHASE_2, 12000); - events.ScheduleEvent(EVENT_REBIRTH, 16001); - } - } - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type != POINT_MOTION_TYPE) - { - if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized() && !me->IsInCombat()) - startPath = true; - return; - } - - if (id == POINT_PLATFORM) - me->setAttackTimer(BASE_ATTACK, 1000); - else if (id == POINT_QUILL) - events.ScheduleEvent(EVENT_START_QUILLS, 1000); - else if (id == POINT_DIVE) - { - events.ScheduleEvent(EVENT_START_DIVE, 1000); - events.ScheduleEvent(EVENT_CAST_DIVE_BOMB, 5000); - } - } - - void UpdateAI(uint32 diff) override - { - if (startPath) - { - me->StopMoving(); - startPath = false; - if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath())) - { - Movement::PointsArray pathPoints; - pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); - for (uint8 i = 0; i < i_path->size(); ++i) - { - WaypointData const* node = i_path->at(i); - pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z)); - } - me->GetMotionMaster()->MoveSplinePath(&pathPoints); - } - } - - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_SWITCH_PLATFORM: - if (roll_chance_i(20 * noQuillTimes)) - { - noQuillTimes = 0; - platform = RAND(0, 3); - me->GetMotionMaster()->MovePoint(POINT_QUILL, alarPoints[POINT_QUILL], false, true); - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 16000); - } - else - { - if (noQuillTimes++ > 0) - { - me->SetOrientation(alarPoints[platform].GetOrientation()); - me->SummonCreature(NPC_EMBER_OF_ALAR, *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - } - me->GetMotionMaster()->MovePoint(POINT_PLATFORM, alarPoints[platform], false, true); - platform = (platform + 1) % 4; - events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 30000); - } - me->setAttackTimer(BASE_ATTACK, 20000); - break; - case EVENT_START_QUILLS: - me->CastSpell(me, SPELL_FLAME_QUILLS, false); - break; - case EVENT_RELOCATE_MIDDLE: - me->SetPosition(alarPoints[POINT_MIDDLE]); - break; - case EVENT_MOVE_TO_PHASE_2: - me->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); - me->CastSpell(me, SPELL_REBIRTH_PHASE2, false); - break; - case EVENT_REBIRTH: - me->SetReactState(REACT_AGGRESSIVE); - platform = POINT_MIDDLE; - me->GetMotionMaster()->MoveChase(me->GetVictim()); - - events.ScheduleEvent(EVENT_SPELL_MELT_ARMOR, 67000); - events.ScheduleEvent(EVENT_SPELL_CHARGE, 10000); - events.ScheduleEvent(EVENT_SPELL_FLAME_PATCH, 20000); - events.ScheduleEvent(EVENT_SPELL_DIVE_BOMB, 30000); - break; - case EVENT_SPELL_MELT_ARMOR: - me->CastSpell(me->GetVictim(), SPELL_MELT_ARMOR, false); - events.ScheduleEvent(EVENT_SPELL_MELT_ARMOR, 60000); - break; - case EVENT_SPELL_CHARGE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) - me->CastSpell(target, SPELL_CHARGE, false); - events.ScheduleEvent(EVENT_SPELL_CHARGE, 30000); - break; - case EVENT_SPELL_FLAME_PATCH: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) - me->SummonCreature(NPC_FLAME_PATCH, *target, TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_SPELL_FLAME_PATCH, 30000); - break; - case EVENT_SPELL_DIVE_BOMB: - me->GetMotionMaster()->MovePoint(POINT_DIVE, alarPoints[POINT_DIVE], false, true); - events.ScheduleEvent(EVENT_SPELL_DIVE_BOMB, 30000); - events.DelayEvents(15000); - me->setAttackTimer(BASE_ATTACK, 20000); - break; - case EVENT_START_DIVE: - me->CastSpell(me, SPELL_DIVE_BOMB_VISUAL, false); - break; - case EVENT_CAST_DIVE_BOMB: - events.ScheduleEvent(EVENT_SUMMON_DIVE_PHOENIX, 2000); - events.ScheduleEvent(EVENT_REBIRTH_DIVE, 6000); - events.ScheduleEvent(EVENT_FINISH_DIVE, 10000); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 90.0f, true)) - { - me->CastSpell(target, SPELL_DIVE_BOMB, false); - me->SetPosition(*target); - me->StopMovingOnCurrentPos(); - } - - me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); - break; - case EVENT_SUMMON_DIVE_PHOENIX: - { - Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 10.0f, true); - me->SummonCreature(NPC_EMBER_OF_ALAR, target ? *target : *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - me->SummonCreature(NPC_EMBER_OF_ALAR, target ? *target : *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); - break; - } - case EVENT_REBIRTH_DIVE: - me->SetModelVisible(true); - me->CastSpell(me, SPELL_REBIRTH_DIVE, false); - break; - case EVENT_FINISH_DIVE: - me->GetMotionMaster()->MoveChase(me->GetVictim()); - break; - case EVENT_SPELL_BERSERK: - me->CastSpell(me, SPELL_BERSERK, true); - break; - } - - if (me->isAttackReady()) - { - if (me->IsWithinMeleeRange(me->GetVictim())) - { - me->AttackerStateUpdate(me->GetVictim()); - me->resetAttackTimer(); - } - else - { - me->resetAttackTimer(); - ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) - if (me->IsWithinMeleeRange(unit)) - { - me->AttackerStateUpdate(unit); - return; - } - - me->CastSpell(me, SPELL_FLAME_BUFFET, false); - } - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetTheEyeAI(creature); + SetCombatMovement(false); } + + void JustReachedHome() override + { + BossAI::JustReachedHome(); + if (me->IsEngaged()) + { + ConstructWaypointsAndMove(); + } + } + + void Reset() override + { + BossAI::Reset(); + _canAttackCooldown = true; + _baseAttackOverride = false; + _spawnPhoenixes = false; + _platform = 0; + _platformRoll = 0; + _noQuillTimes = 0; + _platformMoveRepeatTimer = 16s; + me->SetModelVisible(true); + me->SetReactState(REACT_AGGRESSIVE); + ConstructWaypointsAndMove(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + ScheduleTimedEvent(0s, [&] + { + if (roll_chance_i(20 * _noQuillTimes)) + { + _noQuillTimes = 0; + _platform = RAND(0, 5); + me->GetMotionMaster()->MovePoint(POINT_QUILL, alarPoints[POINT_QUILL], false, true); + _platformMoveRepeatTimer = 16s; + } + else + { + if (_noQuillTimes++ > 0) + { + me->SetOrientation(alarPoints[_platform].GetOrientation()); + if (_spawnPhoenixes) + { + SpawnPhoenixes(3, me); + } + } + me->GetMotionMaster()->MovePoint(POINT_PLATFORM, alarPoints[_platform], false, true); + _platformRoll = RAND(0, 2); + switch(_platformRoll) + { + case DIRECTION_ANTI_CLOCKWISE: + _platform = (_platform+5)%6; + _spawnPhoenixes = false; + break; + case DIRECTION_CLOCKWISE: + _platform = (_platform+1)%6; + _spawnPhoenixes = false; + break; + case DIRECTION_ACROSS: + _platform = (_platform+3)%6; + _spawnPhoenixes = true; + break; + } + _platformMoveRepeatTimer = 30s; + } + }, _platformMoveRepeatTimer); + ScheduleMainSpellAttack(0s); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + me->SetModelVisible(true); + + if (Map* map = me->GetMap()) + { + map->DoForAllPlayers([&](Player* player) + { + if (player->GetQuestStatus(QUEST_RUSE_OF_THE_ASHTONGUE) == QUEST_STATUS_INCOMPLETE) + { + if (player->HasAura(SPELL_ASHTONGUE_RUSE)) + { + player->AreaExploredOrEventHappens(QUEST_RUSE_OF_THE_ASHTONGUE); + } + } + }); + } + } + + void MoveInLineOfSight(Unit* /*who*/) override { } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + if (damage >= me->GetHealth() && _platform < POINT_MIDDLE) + { + damage = 0; + me->InterruptNonMeleeSpells(false); + me->SetHealth(me->GetMaxHealth()); + me->SetReactState(REACT_PASSIVE); + DoCastSelf(SPELL_CLEAR_ALL_DEBUFFS); + DoCastSelf(SPELL_EMBER_BLAST, true); + scheduler.CancelAll(); + ScheduleUniqueTimedEvent(8s, [&]{ + me->SetPosition(alarPoints[POINT_MIDDLE]); + }, EVENT_RELOCATE_MIDDLE); + ScheduleUniqueTimedEvent(12s, [&] + { + me->RemoveAurasDueToSpell(SPELL_EMBER_BLAST); + DoCastSelf(SPELL_REBIRTH_PHASE2); + }, EVENT_MOVE_TO_PHASE_2); + ScheduleUniqueTimedEvent(16001ms, [&]{ + me->SetReactState(REACT_AGGRESSIVE); + _platform = POINT_MIDDLE; + me->GetMotionMaster()->MoveChase(me->GetVictim()); + ScheduleAbilities(); + }, EVENT_REBIRTH); + + } + } + + void ScheduleAbilities() + { + ScheduleTimedEvent(57s, [&] + { + DoCastVictim(SPELL_MELT_ARMOR); + }, 60s); + ScheduleTimedEvent(10s, [&] + { + DoCastRandomTarget(SPELL_CHARGE, 0, 50.0f); + }, 30s); + ScheduleTimedEvent(20s, [&] + { + // find spell from sniffs? + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) + { + me->SummonCreature(NPC_FLAME_PATCH, *target, TEMPSUMMON_TIMED_DESPAWN, 2 * MINUTE * IN_MILLISECONDS); + } + }, 30s); + ScheduleTimedEvent(30s, [&] + { + me->GetMotionMaster()->MovePoint(POINT_DIVE, alarPoints[POINT_DIVE], false, true); + scheduler.DelayAll(15s); + }, 30s); + ScheduleUniqueTimedEvent(10min, [&] + { + DoCastSelf(SPELL_BERSERK); + }, EVENT_SPELL_BERSERK); + ScheduleMainSpellAttack(0s); + } + + void SpawnPhoenixes(uint8 count, Unit* targetToSpawnAt) + { + if (targetToSpawnAt) + { + for (uint8 i = 0; i < count; ++i) + { + me->SummonCreature(NPC_EMBER_OF_ALAR, *targetToSpawnAt, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000); + } + } + } + + void DoDiveBomb() + { + scheduler.Schedule(2s, [this](TaskContext) + { + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 10.0f, true)) + { + SpawnPhoenixes(2, target); + } + }).Schedule(6s, [this](TaskContext) + { + me->SetModelVisible(true); + DoCastSelf(SPELL_REBIRTH_DIVE); + }).Schedule(10s, [this](TaskContext) + { + me->GetMotionMaster()->MoveChase(me->GetVictim()); + }); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 90.0f, true)) + { + DoCast(target, SPELL_DIVE_BOMB); + me->SetPosition(*target); + me->StopMovingOnCurrentPos(); + } + me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL); + + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type != POINT_MOTION_TYPE) + { + if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized() && !me->IsInCombat()) + { + ConstructWaypointsAndMove(); + } + return; + } + + switch(id) + { + case POINT_QUILL: + scheduler.CancelGroup(GROUP_FLAME_BUFFET); + scheduler.Schedule(1s, [this](TaskContext) + { + DoCastSelf(SPELL_FLAME_QUILLS); + }); + ScheduleMainSpellAttack(13s); + break; + case POINT_DIVE: + scheduler.Schedule(1s, [this](TaskContext) + { + DoCastSelf(SPELL_DIVE_BOMB_VISUAL); + }).Schedule(5s, [this](TaskContext) + { + DoDiveBomb(); + }); + break; + default: + return; + } + } + + void ScheduleMainSpellAttack(std::chrono::seconds timer) + { + scheduler.Schedule(timer, GROUP_FLAME_BUFFET, [this](TaskContext context) + { + if (!me->IsWithinMeleeRange(me->GetVictim()) && !me->isMoving()) + { + DoCastVictim(SPELL_FLAME_BUFFET); + } + context.Repeat(2s); + }); + } + + void ConstructWaypointsAndMove() + { + me->StopMoving(); + if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath())) + { + Movement::PointsArray pathPoints; + pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ())); + for (uint8 i = 0; i < i_path->size(); ++i) + { + WaypointData const* node = i_path->at(i); + pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z)); + } + me->GetMotionMaster()->MoveSplinePath(&pathPoints); + } + } + +private: + bool _canAttackCooldown; + bool _baseAttackOverride; + bool _spawnPhoenixes; + uint8 _platform; + uint8 _platformRoll; + uint8 _noQuillTimes; + std::chrono::seconds _platformMoveRepeatTimer; }; class CastQuill : public BasicEvent { public: - CastQuill(Unit* caster, uint32 spellId) : _caster(caster), _spellId(spellId) - { - } + CastQuill(Unit* caster, uint32 spellId) : _caster(caster), _spellId(spellId){ } bool Execute(uint64 /*execTime*/, uint32 /*diff*/) override { _caster->CastSpell(_caster, _spellId, true); return true; } - private: Unit* _caster; uint32 _spellId; @@ -496,7 +507,7 @@ public: void AddSC_boss_alar() { - new boss_alar(); + RegisterTheEyeAI(boss_alar); new spell_alar_flame_quills(); new spell_alar_ember_blast(); new spell_alar_ember_blast_death(); From af880d516f78d9b23ebaf620f5f328751ae823be Mon Sep 17 00:00:00 2001 From: Dan <83884799+elthehablo@users.noreply.github.com> Date: Thu, 8 Feb 2024 03:43:38 +0100 Subject: [PATCH 05/17] refactor(Scripts/ZulAman): refactor Halazzi (#18267) * initial * some fixes * last booboos * fixes after test * finale * actual final * blegh --- .../updates/pending_db_world/root_totem.sql | 4 + .../EasternKingdoms/ZulAman/boss_halazzi.cpp | 514 ++++++++---------- .../ZulAman/instance_zulaman.cpp | 13 + .../scripts/EasternKingdoms/ZulAman/zulaman.h | 12 +- 4 files changed, 258 insertions(+), 285 deletions(-) create mode 100644 data/sql/updates/pending_db_world/root_totem.sql diff --git a/data/sql/updates/pending_db_world/root_totem.sql b/data/sql/updates/pending_db_world/root_totem.sql new file mode 100644 index 000000000..77acc4594 --- /dev/null +++ b/data/sql/updates/pending_db_world/root_totem.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `creature_template_movement` WHERE `CreatureId` = 24224; +INSERT INTO `creature_template_movement` (`CreatureId`, `Flight`, `Rooted`) VALUES +(24224, 1, 1); diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp index 6dd0b436c..53d2c3d2d 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_halazzi.cpp @@ -37,9 +37,13 @@ enum Spells SPELL_SHRED_ARMOR = 43243 // Used by Spirit Lynx }; +enum UniqueEvents +{ + EVENT_BERSERK = 0 +}; + enum Hal_CreatureIds { - NPC_SPIRIT_LYNX = 24143, NPC_TOTEM = 24224 }; @@ -63,326 +67,274 @@ enum Yells SAY_DEATH = 5 }; -class boss_halazzi : public CreatureScript +enum Groups { -public: - boss_halazzi() : CreatureScript("boss_halazzi") { } + GROUP_LYNX = 0, + GROUP_HUMAN = 1, + GROUP_MERGE = 2 +}; - struct boss_halazziAI : public ScriptedAI +struct boss_halazzi : public BossAI +{ + boss_halazzi(Creature* creature) : BossAI(creature, DATA_HALAZZIEVENT) { - boss_halazziAI(Creature* creature) : ScriptedAI(creature), summons(me) + scheduler.SetValidator([this] { - instance = creature->GetInstanceScript(); + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } + + void Reset() override + { + BossAI::Reset(); + _transformCount = 0; + _healthCheckPercentage = 0; + _phase = PHASE_NONE; + _lynxFormHealth = me->GetMaxHealth(); + _healthPortion = _lynxFormHealth/4; + _humanFormHealth = (me->GetMaxHealth())/0.66666666; + EnterPhase(PHASE_LYNX); + DoCastSelf(SPELL_DUAL_WIELD, true); + } + + void JustSummoned(Creature* summon) override + { + BossAI::JustSummoned(summon); + summon->Attack(me->GetVictim(), false); + summon->SetInCombatWithZone(); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO); + ScheduleUniqueTimedEvent(10min, [&] + { + DoCastSelf(SPELL_BERSERK, true); + }, EVENT_BERSERK); + EnterPhase(PHASE_LYNX); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override + { + if (damage >= me->GetHealth() && _phase != PHASE_ENRAGE) + { + damage = 0; } - - InstanceScript* instance; - SummonList summons; - PhaseHalazzi Phase; - - uint32 FrenzyTimer; - uint32 SaberlashTimer; - uint32 ShockTimer; - uint32 TotemTimer; - uint32 CheckTimer; - uint32 BerserkTimer; - uint32 TransformCount; - - ObjectGuid LynxGUID; - - void Reset() override + else { - instance->SetData(DATA_HALAZZIEVENT, NOT_STARTED); - summons.DespawnAll(); - - LynxGUID.Clear(); - TransformCount = 0; - BerserkTimer = 600000; - CheckTimer = 1000; - - DoCast(me, SPELL_DUAL_WIELD, true); - - Phase = PHASE_NONE; - EnterPhase(PHASE_LYNX); - } - - void JustEngagedWith(Unit* /*who*/) override - { - instance->SetData(DATA_HALAZZIEVENT, IN_PROGRESS); - Talk(SAY_AGGRO); - EnterPhase(PHASE_LYNX); - } - - void JustSummoned(Creature* summon) override - { - summon->AI()->AttackStart(me->GetVictim()); - if (summon->GetEntry() == NPC_SPIRIT_LYNX) - LynxGUID = summon->GetGUID(); - summons.Summon(summon); - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth() && Phase != PHASE_ENRAGE) - damage = 0; - } - - void SpellHit(Unit*, SpellInfo const* spell) override - { - if (spell->Id == SPELL_TRANSFORM_SPLIT2) - EnterPhase(PHASE_HUMAN); - } - - void AttackStart(Unit* who) override - { - if (Phase != PHASE_MERGE) - ScriptedAI::AttackStart(who); - } - - void EnterPhase(PhaseHalazzi NextPhase) - { - switch (NextPhase) + if (_phase == PHASE_LYNX || _phase == PHASE_ENRAGE) { - case PHASE_LYNX: - case PHASE_ENRAGE: - if (Phase == PHASE_MERGE) - { - DoCast(me, SPELL_TRANSFORM_MERGE, true); - me->Attack(me->GetVictim(), true); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - } - if (Creature* Lynx = ObjectAccessor::GetCreature(*me, LynxGUID)) - Lynx->DisappearAndDie(); - me->SetMaxHealth(600000); - me->SetHealth(600000 - 150000 * TransformCount); - FrenzyTimer = 16000; - SaberlashTimer = 20000; - ShockTimer = 10000; - TotemTimer = 12000; - break; - case PHASE_SPLIT: - Talk(SAY_SPLIT); - DoCast(me, SPELL_TRANSFORM_SPLIT, true); - break; - case PHASE_HUMAN: - //DoCast(me, SPELL_SUMMON_LYNX, true); - DoSpawnCreature(NPC_SPIRIT_LYNX, 5, 5, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); - me->SetMaxHealth(400000); - me->SetHealth(400000); - ShockTimer = 10000; - TotemTimer = 12000; - break; - case PHASE_MERGE: - if (Unit* pLynx = ObjectAccessor::GetUnit(*me, LynxGUID)) - { - Talk(SAY_MERGE); - pLynx->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); - pLynx->GetMotionMaster()->Clear(); - pLynx->GetMotionMaster()->MoveFollow(me, 0, 0); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveFollow(pLynx, 0, 0); - ++TransformCount; - } - break; - default: - break; + _healthCheckPercentage = 25 * (3 - _transformCount); + if (!HealthAbovePct(_healthCheckPercentage)) + { + EnterPhase(PHASE_SPLIT); + } } - Phase = NextPhase; - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (BerserkTimer <= diff) + else if (_phase == PHASE_HUMAN) { - DoCast(me, SPELL_BERSERK, true); - BerserkTimer = 60000; + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + if (!HealthAbovePct(20) || !lynx->HealthAbovePct(20)) + { + EnterPhase(PHASE_MERGE); + } + } + else + { + //should not really happen + EnterEvadeMode(); + } } - else BerserkTimer -= diff; + } + } - if (Phase == PHASE_LYNX || Phase == PHASE_ENRAGE) - { - if (SaberlashTimer <= diff) + void SpellHit(Unit*, SpellInfo const* spell) override + { + if (spell->Id == SPELL_TRANSFORM_SPLIT2) + { + EnterPhase(PHASE_HUMAN); + } + } + + void AttackStart(Unit* who) override + { + if (_phase != PHASE_MERGE) + { + BossAI::AttackStart(who); + } + } + + void EnterPhase(PhaseHalazzi nextPhase) + { + switch (nextPhase) + { + case PHASE_LYNX: + case PHASE_ENRAGE: + if (_phase == PHASE_MERGE) + { + DoCastSelf(SPELL_TRANSFORM_MERGE, true); + me->RemoveAurasDueToSpell(SPELL_TRANSFORM_SPLIT2); + me->GetMotionMaster()->MoveChase(me->GetVictim()); + } + summons.DespawnAll(); + me->SetMaxHealth(_lynxFormHealth); + me->SetHealth(_lynxFormHealth - _healthPortion * _transformCount); + scheduler.CancelGroup(GROUP_MERGE); + scheduler.Schedule(16s, GROUP_LYNX, [this](TaskContext context) + { + DoCastSelf(SPELL_FRENZY); + context.Repeat(10s, 15s); + }).Schedule(20s, GROUP_LYNX, [this](TaskContext context) { Talk(SAY_SABER); - // A tank with more than 490 defense skills should receive no critical hit - //DoCast(me, 41296, true); DoCastVictim(SPELL_SABER_LASH, true); - //me->RemoveAurasDueToSpell(41296); - SaberlashTimer = 30000; - } - else SaberlashTimer -= diff; - - if (FrenzyTimer <= diff) - { - DoCast(me, SPELL_FRENZY); - FrenzyTimer = urand(10000, 15000); - } - else FrenzyTimer -= diff; - - if (Phase == PHASE_LYNX) - { - if (CheckTimer <= diff) - { - if (HealthBelowPct(25 * (3 - TransformCount))) - EnterPhase(PHASE_SPLIT); - CheckTimer = 1000; - } - else CheckTimer -= diff; - } - } - - if (Phase == PHASE_HUMAN || Phase == PHASE_ENRAGE) - { - if (TotemTimer <= diff) - { - DoCast(me, SPELL_SUMMON_TOTEM); - TotemTimer = 20000; - } - else TotemTimer -= diff; - - if (ShockTimer <= diff) + context.Repeat(30s); + }); + break; + case PHASE_SPLIT: + Talk(SAY_SPLIT); + DoCastSelf(SPELL_TRANSFORM_SPLIT, true); + break; + case PHASE_HUMAN: + scheduler.CancelGroup(GROUP_MERGE); + DoCastSelf(SPELL_SUMMON_LYNX, true); + me->SetMaxHealth(_humanFormHealth); + me->SetHealth(_humanFormHealth); + scheduler.CancelGroup(GROUP_LYNX); + scheduler.Schedule(10s, GROUP_HUMAN, [this](TaskContext context) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0)) { if (target->IsNonMeleeSpellCast(false)) + { DoCast(target, SPELL_EARTHSHOCK); + } else + { DoCast(target, SPELL_FLAMESHOCK); - ShockTimer = urand(10000, 15000); - } - } - else ShockTimer -= diff; - - if (Phase == PHASE_HUMAN) - { - if (CheckTimer <= diff) - { - if (!HealthAbovePct(20) /*HealthBelowPct(10)*/) - EnterPhase(PHASE_MERGE); - else - { - Unit* Lynx = ObjectAccessor::GetUnit(*me, LynxGUID); - if (Lynx && !Lynx->HealthAbovePct(20) /*Lynx->HealthBelowPct(10)*/) - EnterPhase(PHASE_MERGE); - } - CheckTimer = 1000; - } - else CheckTimer -= diff; - } - } - - if (Phase == PHASE_MERGE) - { - if (CheckTimer <= diff) - { - Unit* Lynx = ObjectAccessor::GetUnit(*me, LynxGUID); - if (Lynx) - { - Lynx->GetMotionMaster()->MoveFollow(me, 0, 0); - me->GetMotionMaster()->MoveFollow(Lynx, 0, 0); - if (me->IsWithinDistInMap(Lynx, 6.0f)) - { - if (TransformCount < 3) - EnterPhase(PHASE_LYNX); - else - EnterPhase(PHASE_ENRAGE); } } - CheckTimer = 1000; + context.Repeat(10s, 15s); + }).Schedule(12s, GROUP_HUMAN, [this](TaskContext context) + { + DoCastSelf(SPELL_SUMMON_TOTEM); + context.Repeat(20s); + }); + break; + case PHASE_MERGE: + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + Talk(SAY_MERGE); + scheduler.CancelGroup(GROUP_HUMAN); + lynx->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + lynx->GetMotionMaster()->Clear(); + lynx->GetMotionMaster()->MoveFollow(me, 0, 0); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveFollow(lynx, 0, 0); + ++_transformCount; + scheduler.Schedule(2s, GROUP_MERGE, [this](TaskContext context) + { + if (Creature* lynx = instance->GetCreature(DATA_SPIRIT_LYNX)) + { + if (me->IsWithinDistInMap(lynx, 6.0f)) + { + if (_transformCount < 3) + { + EnterPhase(PHASE_LYNX); + } + else + { + EnterPhase(PHASE_ENRAGE); + } + } + } + context.Repeat(2s); + }); } - else CheckTimer -= diff; - } - - DoMeleeAttackIfReady(); + break; + default: + break; } + _phase = nextPhase; + } - void KilledUnit(Unit* victim) override + void KilledUnit(Unit* victim) override + { + BossAI::KilledUnit(victim); + if (victim->IsPlayer()) { - if (victim->GetTypeId() != TYPEID_PLAYER) - return; - Talk(SAY_KILL); } - - void JustDied(Unit* /*killer*/) override - { - instance->SetData(DATA_HALAZZIEVENT, DONE); - Talk(SAY_DEATH); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetZulAmanAI(creature); } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH); + } +private: + uint32 _lynxFormHealth; + uint32 _humanFormHealth; + uint32 _healthPortion; + uint8 _transformCount; + uint32 _healthCheckPercentage; + PhaseHalazzi _phase; }; - // Spirits Lynx AI -class npc_halazzi_lynx : public CreatureScript +struct npc_halazzi_lynx : public ScriptedAI { -public: - npc_halazzi_lynx() : CreatureScript("npc_halazzi_lynx") { } + npc_halazzi_lynx(Creature* creature) : ScriptedAI(creature) { } - struct npc_halazzi_lynxAI : public ScriptedAI + void Reset() override { - npc_halazzi_lynxAI(Creature* creature) : ScriptedAI(creature) { } + scheduler.CancelAll(); + } - uint32 FrenzyTimer; - uint32 shredder_timer; - - void Reset() override - { - FrenzyTimer = urand(30000, 50000); //frenzy every 30-50 seconds - shredder_timer = 4000; - } - - void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override - { - if (damage >= me->GetHealth()) - damage = 0; - } - - void AttackStart(Unit* who) override - { - if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) - ScriptedAI::AttackStart(who); - } - - void JustEngagedWith(Unit* /*who*/) override {/*DoZoneInCombat();*/ } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - if (FrenzyTimer <= diff) - { - DoCast(me, SPELL_LYNX_FRENZY); - FrenzyTimer = urand(30000, 50000); //frenzy every 30-50 seconds - } - else FrenzyTimer -= diff; - - if (shredder_timer <= diff) - { - DoCastVictim(SPELL_SHRED_ARMOR); - shredder_timer = 4000; - } - else shredder_timer -= diff; - - DoMeleeAttackIfReady(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override { - return GetZulAmanAI(creature); + if (damage >= me->GetHealth()) + { + damage = 0; + } + } + + void AttackStart(Unit* who) override + { + if (!me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) + { + ScriptedAI::AttackStart(who); + } + } + + void JustEngagedWith(Unit* who) override + { + ScriptedAI::JustEngagedWith(who); + + ScheduleTimedEvent(30s, 50s, [&] + { + DoCastSelf(SPELL_LYNX_FRENZY); + }, 30s, 50s); + ScheduleTimedEvent(4s, [&]{ + DoCastVictim(SPELL_SHRED_ARMOR); + }, 4s); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + { + return; + } + + scheduler.Update(diff); + + DoMeleeAttackIfReady(); } }; void AddSC_boss_halazzi() { - new boss_halazzi(); - new npc_halazzi_lynx(); + RegisterZulAmanCreatureAI(boss_halazzi); + RegisterZulAmanCreatureAI(npc_halazzi_lynx); } diff --git a/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp index ea7198184..baa83f557 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/instance_zulaman.cpp @@ -55,6 +55,17 @@ static SHostageInfo HostageInfo[] = Position const HarrisonJonesLoc = {120.687f, 1674.0f, 42.0217f, 1.59044f}; +ObjectData const creatureData[] = +{ + { NPC_SPIRIT_LYNX, DATA_SPIRIT_LYNX }, + { 0, 0 } +}; + +ObjectData const gameObjectData[] = +{ + { 0, 0 } +}; + class instance_zulaman : public InstanceMapScript { public: @@ -92,6 +103,7 @@ public: void Initialize() override { SetHeaders(DataHeader); + LoadObjectData(creatureData, gameObjectData); memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); QuestTimer = 0; @@ -135,6 +147,7 @@ public: default: break; } + InstanceScript::OnCreatureCreate(creature); } void OnGameObjectCreate(GameObject* go) override diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h index d8a5fdd1c..4ca81145b 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h +++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.h @@ -32,9 +32,10 @@ enum DataTypes DATA_HALAZZIEVENT = 4, DATA_HEXLORDEVENT = 5, DATA_ZULJINEVENT = 6, - DATA_CHESTLOOTED = 7, - TYPE_RAND_VENDOR_1 = 8, - TYPE_RAND_VENDOR_2 = 9 + DATA_SPIRIT_LYNX = 7, + DATA_CHESTLOOTED = 8, + TYPE_RAND_VENDOR_1 = 9, + TYPE_RAND_VENDOR_2 = 10 }; enum CreatureIds @@ -44,7 +45,8 @@ enum CreatureIds NPC_ZULJIN = 23863, NPC_HEXLORD = 24239, NPC_HALAZZI = 23577, - NPC_NALORAKK = 23576 + NPC_NALORAKK = 23576, + NPC_SPIRIT_LYNX = 24143 }; enum GameobjectIds @@ -68,4 +70,6 @@ inline AI* GetZulAmanAI(T* obj) return GetInstanceAI(obj, ZulAmanScriptName); } +#define RegisterZulAmanCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetZulAmanAI) + #endif From c841ec5b745eec3cee8576ab2768df500d296d65 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 Feb 2024 02:44:15 +0000 Subject: [PATCH 06/17] chore(DB): import pending files Referenced commit(s): bc25ade49825501aaee80b9da79ce14b1e7839cc --- .../root_totem.sql => db_world/2024_02_08_00.sql} | 1 + .../2024_02_08_01.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/root_totem.sql => db_world/2024_02_08_00.sql} (79%) rename data/sql/updates/{pending_db_world/spell_immunity_alar_and_adds.sql => db_world/2024_02_08_01.sql} (73%) diff --git a/data/sql/updates/pending_db_world/root_totem.sql b/data/sql/updates/db_world/2024_02_08_00.sql similarity index 79% rename from data/sql/updates/pending_db_world/root_totem.sql rename to data/sql/updates/db_world/2024_02_08_00.sql index 77acc4594..7e810ffad 100644 --- a/data/sql/updates/pending_db_world/root_totem.sql +++ b/data/sql/updates/db_world/2024_02_08_00.sql @@ -1,3 +1,4 @@ +-- DB update 2024_02_06_00 -> 2024_02_08_00 -- DELETE FROM `creature_template_movement` WHERE `CreatureId` = 24224; INSERT INTO `creature_template_movement` (`CreatureId`, `Flight`, `Rooted`) VALUES diff --git a/data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql b/data/sql/updates/db_world/2024_02_08_01.sql similarity index 73% rename from data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql rename to data/sql/updates/db_world/2024_02_08_01.sql index 4143a0738..c2f3d42c2 100644 --- a/data/sql/updates/pending_db_world/spell_immunity_alar_and_adds.sql +++ b/data/sql/updates/db_world/2024_02_08_01.sql @@ -1,2 +1,3 @@ +-- DB update 2024_02_08_00 -> 2024_02_08_01 -- UPDATE `creature_template` SET `spell_school_immune_mask` = `spell_school_immune_mask`|4 WHERE `entry` IN (19514, 19551); From 65d6bd0472ba3a60bbd5667e581e342d5a72b4ae Mon Sep 17 00:00:00 2001 From: Kalimist Date: Thu, 8 Feb 2024 01:41:23 -0500 Subject: [PATCH 07/17] fix(Core/Creature): hardcoded gossip in zone_azshara (#18289) * Changing hardcoded text in #define to enum member - Changing #define to new enum member for AddGossipItemFor method * Adding SQL Query File * Forgot to add ';' in DELETE FROM --- .../sql/updates/pending_db_world/Rev_1629488923927348.sql | 3 +++ src/server/scripts/Kalimdor/zone_azshara.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 data/sql/updates/pending_db_world/Rev_1629488923927348.sql diff --git a/data/sql/updates/pending_db_world/Rev_1629488923927348.sql b/data/sql/updates/pending_db_world/Rev_1629488923927348.sql new file mode 100644 index 000000000..ffa3d3d81 --- /dev/null +++ b/data/sql/updates/pending_db_world/Rev_1629488923927348.sql @@ -0,0 +1,3 @@ +DELETE FROM `gossip_menu_option` WHERE `MenuID`=21893; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(21893, 0, 0, 'Hand over the Southfury moonstone and I\'ll let you go.', 20723, 1, 1, 0, 0, 0, 0, '', 0, 0); diff --git a/src/server/scripts/Kalimdor/zone_azshara.cpp b/src/server/scripts/Kalimdor/zone_azshara.cpp index e225cff10..8fbb8f62a 100644 --- a/src/server/scripts/Kalimdor/zone_azshara.cpp +++ b/src/server/scripts/Kalimdor/zone_azshara.cpp @@ -53,10 +53,10 @@ enum RizzleSprysprocketData SAY_RIZZLE_START = 0, SAY_RIZZLE_GRENADE = 1, SAY_RIZZLE_FINAL = 2, - MSG_ESCAPE_NOTICE = 3 -}; + MSG_ESCAPE_NOTICE = 3, + GOSSIP_GET_MOONSTONE = 21893 -#define GOSSIP_GET_MOONSTONE "Hand over the Southfury moonstone and I'll let you go." +}; Position const WPs[58] = { @@ -295,7 +295,7 @@ public: if (player->GetQuestStatus(QUEST_CHASING_THE_MOONSTONE) != QUEST_STATUS_INCOMPLETE) return true; - AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_GET_MOONSTONE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + AddGossipItemFor(player, GOSSIP_GET_MOONSTONE, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); SendGossipMenuFor(player, 10811, creature->GetGUID()); return true; From 51c414b9d58c224c1a2238f8bfc07cbed0060a02 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 Feb 2024 06:42:14 +0000 Subject: [PATCH 08/17] chore(DB): import pending files Referenced commit(s): 65d6bd0472ba3a60bbd5667e581e342d5a72b4ae --- .../Rev_1629488923927348.sql => db_world/2024_02_08_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Rev_1629488923927348.sql => db_world/2024_02_08_02.sql} (90%) diff --git a/data/sql/updates/pending_db_world/Rev_1629488923927348.sql b/data/sql/updates/db_world/2024_02_08_02.sql similarity index 90% rename from data/sql/updates/pending_db_world/Rev_1629488923927348.sql rename to data/sql/updates/db_world/2024_02_08_02.sql index ffa3d3d81..c50f508dc 100644 --- a/data/sql/updates/pending_db_world/Rev_1629488923927348.sql +++ b/data/sql/updates/db_world/2024_02_08_02.sql @@ -1,3 +1,4 @@ +-- DB update 2024_02_08_01 -> 2024_02_08_02 DELETE FROM `gossip_menu_option` WHERE `MenuID`=21893; INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES (21893, 0, 0, 'Hand over the Southfury moonstone and I\'ll let you go.', 20723, 1, 1, 0, 0, 0, 0, '', 0, 0); From d42e04097c45b3c6b79072ac3e79eaf826be8e65 Mon Sep 17 00:00:00 2001 From: Dan <83884799+elthehablo@users.noreply.github.com> Date: Thu, 8 Feb 2024 10:05:38 +0100 Subject: [PATCH 09/17] fix(Scripts/TheEye): Solarian movement (#18268) * first implementation * better implementation * don't move before first spell * chase in p2 again * Update boss_astromancer.cpp * oops --- .../Outland/TempestKeep/Eye/boss_astromancer.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp index bbceb7b97..d04b9055b 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp @@ -68,6 +68,7 @@ struct boss_high_astromancer_solarian : public BossAI ScheduleHealthCheckEvent(20, [&]{ scheduler.CancelAll(); + me->ResumeChasingVictim(); scheduler.Schedule(3s, [this](TaskContext context) { DoCastVictim(SPELL_VOID_BOLT); @@ -109,10 +110,21 @@ struct boss_high_astromancer_solarian : public BossAI Talk(SAY_AGGRO); BossAI::JustEngagedWith(who); me->CallForHelp(105.0f); + me->GetMotionMaster()->Clear(); scheduler.Schedule(3650ms, [this](TaskContext context) { - DoCastRandomTarget(SPELL_ARCANE_MISSILES, 0, 40.0f); + me->GetMotionMaster()->Clear(); + if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 40.0f, true)) + { + DoCast(target, SPELL_ARCANE_MISSILES); + } + else + { + //no targets in required range + me->GetMotionMaster()->MoveChase(me->GetVictim(), 30.0f); + me->CastStop(); + } context.Repeat(800ms, 7300ms); }).Schedule(21800ms, [this](TaskContext context) { From ed53ac2febf8cc6009e55150e307a5025df2d8b6 Mon Sep 17 00:00:00 2001 From: Walter Pagani Date: Thu, 8 Feb 2024 06:07:14 -0300 Subject: [PATCH 10/17] chore(deps/boost): Update cmake file compatible with new policies (#18283) --- deps/boost/CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/deps/boost/CMakeLists.txt b/deps/boost/CMakeLists.txt index db4ef4ef5..cb44a0f7f 100644 --- a/deps/boost/CMakeLists.txt +++ b/deps/boost/CMakeLists.txt @@ -12,10 +12,10 @@ if(WIN32) set(BOOST_DEBUG ON) - if(DEFINED ENV{BOOST_ROOT}) - set(BOOST_ROOT $ENV{BOOST_ROOT}) + if(DEFINED ENV{Boost_ROOT}) + set(Boost_ROOT $ENV{Boost_ROOT}) list(APPEND BOOST_LIBRARYDIR - ${BOOST_ROOT}/lib${PLATFORM}-msvc-14.2) + ${Boost_ROOT}/lib${PLATFORM}-msvc-14.2) endif() set(Boost_USE_STATIC_LIBS ON) @@ -35,8 +35,8 @@ endif() find_package(Boost ${BOOST_REQUIRED_VERSION} REQUIRED system filesystem program_options iostreams regex) if(NOT Boost_FOUND) - if(NOT DEFINED ENV{BOOST_ROOT} AND NOT DEFINED Boost_DIR AND NOT DEFINED BOOST_ROOT AND NOT DEFINED BOOSTROOT) - message(FATAL_ERROR "No BOOST_ROOT environment variable could be found! Please make sure it is set and the points to your Boost installation.") + if(NOT DEFINED ENV{Boost_ROOT} AND NOT DEFINED Boost_DIR AND NOT DEFINED BOOST_ROOT AND NOT DEFINED BOOSTROOT) + message(FATAL_ERROR "No Boost_ROOT environment variable could be found! Please make sure it is set and the points to your Boost installation.") endif() endif() From 425a490a7be2ab7bef02d50a45fe17254179bb3b Mon Sep 17 00:00:00 2001 From: Nathan Handley Date: Fri, 9 Feb 2024 03:27:02 -0600 Subject: [PATCH 11/17] feat(Core/Unit): New helper HasActivePowerType and script hook OnPlayerHasActivePowerType (#18293) * Create HasActivePower for script intercept * Replace relevant player-related getPowerType() comparators with HasActivePowerType * Change OnPlayerHasActivePowerType to regular bool instead of optional --------- Co-authored-by: NathanHandley --- src/server/game/Entities/Player/Player.cpp | 41 +++++++++++-------- src/server/game/Entities/Player/Player.h | 2 + src/server/game/Entities/Unit/Unit.cpp | 12 +++--- src/server/game/Entities/Unit/Unit.h | 1 + .../Scripting/ScriptDefines/PlayerScript.cpp | 15 +++++++ .../Scripting/ScriptDefines/PlayerScript.h | 2 + src/server/game/Scripting/ScriptMgr.h | 1 + .../game/Spells/Auras/SpellAuraEffects.cpp | 8 ++-- src/server/game/Spells/Spell.cpp | 4 +- src/server/game/Spells/SpellEffects.cpp | 8 ++-- src/server/scripts/Spells/spell_item.cpp | 2 +- src/server/scripts/Spells/spell_paladin.cpp | 4 +- src/server/scripts/Spells/spell_shaman.cpp | 4 +- 13 files changed, 66 insertions(+), 38 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 11f7178a3..d4736d5f7 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -587,13 +587,13 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo InitPrimaryProfessions(); // to max set before any spell added // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() - if (getPowerType() == POWER_MANA) + if (HasActivePowerType(POWER_MANA)) { UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect) SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); } - if (getPowerType() == POWER_RUNIC_POWER) + if (HasActivePowerType(POWER_RUNIC_POWER)) { SetPower(POWER_RUNE, 8); SetMaxPower(POWER_RUNE, 8); @@ -2019,22 +2019,21 @@ void Player::RegenerateHealth() void Player::ResetAllPowers() { SetHealth(GetMaxHealth()); - switch (getPowerType()) + if (HasActivePowerType(POWER_MANA)) { - case POWER_MANA: - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - break; - case POWER_RAGE: - SetPower(POWER_RAGE, 0); - break; - case POWER_ENERGY: - SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); - break; - case POWER_RUNIC_POWER: - SetPower(POWER_RUNIC_POWER, 0); - break; - default: - break; + SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); + } + if (HasActivePowerType(POWER_RAGE)) + { + SetPower(POWER_RAGE, 0); + } + if (HasActivePowerType(POWER_ENERGY)) + { + SetPower(POWER_ENERGY, GetMaxPower(POWER_ENERGY)); + } + if (HasActivePowerType(POWER_RUNIC_POWER)) + { + SetPower(POWER_RUNIC_POWER, 0); } } @@ -2714,6 +2713,14 @@ void Player::InitStatsForLevel(bool reapplyMods) pet->SynchronizeLevelWithOwner(); } +bool Player::HasActivePowerType(Powers power) +{ + if (sScriptMgr->OnPlayerHasActivePowerType(this, power)) + return true; + else + return (getPowerType() == power); +} + void Player::SendInitialSpells() { uint32 curTime = GameTime::GetGameTimeMS().count(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 5d6494239..bb99778be 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1170,6 +1170,8 @@ public: void InitStatsForLevel(bool reapplyMods = false); + [[nodiscard]] bool HasActivePowerType(Powers power) override; + // .cheat command related [[nodiscard]] bool GetCommandStatus(uint32 command) const { return _activeCheats & command; } void SetCommandStatusOn(uint32 command) { _activeCheats |= command; } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 4b136f63c..b4c6812af 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -929,7 +929,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage } // Rage from Damage made (only from direct weapon damage) - if (attacker && cleanDamage && damagetype == DIRECT_DAMAGE && attacker != victim && attacker->getPowerType() == POWER_RAGE) + if (attacker && cleanDamage && damagetype == DIRECT_DAMAGE && attacker != victim && attacker->HasActivePowerType(POWER_RAGE)) { uint32 weaponSpeedHitFactor; @@ -957,10 +957,10 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage // Rage from absorbed damage if (cleanDamage && cleanDamage->absorbed_damage) { - if (victim->getPowerType() == POWER_RAGE) + if (victim->HasActivePowerType(POWER_RAGE)) victim->RewardRage(cleanDamage->absorbed_damage, 0, false); - if (attacker && attacker->getPowerType() == POWER_RAGE ) + if (attacker && attacker->HasActivePowerType(POWER_RAGE)) attacker->RewardRage(cleanDamage->absorbed_damage, 0, true); } @@ -1083,7 +1083,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage } // Rage from damage received - if (attacker != victim && victim->getPowerType() == POWER_RAGE) + if (attacker != victim && victim->HasActivePowerType(POWER_RAGE)) { uint32 rageDamage = damage + (cleanDamage ? cleanDamage->absorbed_damage : 0); victim->RewardRage(rageDamage, 0, false); @@ -6996,7 +6996,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // Magic Absorption if (dummySpell->SpellIconID == 459) // only this spell has SpellIconID == 459 and dummy aura { - if (getPowerType() != POWER_MANA) + if (!HasActivePowerType(POWER_MANA)) return false; // mana reward @@ -7775,7 +7775,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // Judgement of Wisdom case 20186: { - if (!victim || !victim->IsAlive() || victim->getPowerType() != POWER_MANA || victim->HasSpellCooldown(20268)) + if (!victim || !victim->IsAlive() || !victim->HasActivePowerType(POWER_MANA) || victim->HasSpellCooldown(20268)) return false; // 2% of base mana diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index bf70798d9..ad55d384c 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1472,6 +1472,7 @@ public: [[nodiscard]] Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, 3)); } void setPowerType(Powers power); + [[nodiscard]] virtual bool HasActivePowerType(Powers power) { return getPowerType() == power; } [[nodiscard]] uint32 GetPower(Powers power) const { return GetUInt32Value(static_cast(UNIT_FIELD_POWER1) + power); } [[nodiscard]] uint32 GetMaxPower(Powers power) const { return GetUInt32Value(static_cast(UNIT_FIELD_MAXPOWER1) + power); } void SetPower(Powers power, uint32 val, bool withPowerUpdate = true, bool fromRegenerate = false); diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index ed2465695..c9574106d 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -1046,6 +1046,21 @@ void ScriptMgr::OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, }); } +bool ScriptMgr::OnPlayerHasActivePowerType(Player const* player, Powers power) +{ + auto ret = IsValidBoolScript([&](PlayerScript* script) + { + return script->OnPlayerHasActivePowerType(player, power); + }); + + if (ret && *ret) + { + return true; + } + + return false; +} + void ScriptMgr::OnUpdateGatheringSkill(Player *player, uint32 skillId, uint32 currentLevel, uint32 gray, uint32 green, uint32 yellow, uint32 &gain) { ExecuteScript([&](PlayerScript* script) { diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h index 788474390..68c67fa00 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h @@ -324,6 +324,8 @@ public: virtual void OnGetMaxSkillValue(Player* /*player*/, uint32 /*skill*/, int32& /*result*/, bool /*IsPure*/) { } + [[nodiscard]] virtual bool OnPlayerHasActivePowerType(Player const* /*player*/, Powers /*power*/) { return false; } + /** * @brief This hook called before gathering skill gain is applied to the character. * diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index c39c10980..0df982ea4 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -402,6 +402,7 @@ public: /* PlayerScript */ void OnDeleteFromDB(CharacterDatabaseTransaction trans, uint32 guid); bool CanRepopAtGraveyard(Player* player); void OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, bool IsPure); + bool OnPlayerHasActivePowerType(Player const* player, Powers power); void OnUpdateGatheringSkill(Player* player, uint32 skillId, uint32 currentLevel, uint32 gray, uint32 green, uint32 yellow, uint32& gain); void OnUpdateCraftingSkill(Player* player, SkillLineAbilityEntry const* skill, uint32 currentLevel, uint32& gain); bool OnUpdateFishingSkill(Player* player, int32 skill, int32 zone_skill, int32 chance, int32 roll); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index f8c8117af..130945722 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -6244,7 +6244,7 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const { // Converts up to 10 rage per second into health for $d. Each point of rage is converted into ${$m2/10}.1% of max health. // Should be manauser - if (target->getPowerType() != POWER_RAGE) + if (!target->HasActivePowerType(POWER_RAGE)) break; uint32 rage = target->GetPower(POWER_RAGE); // Nothing todo @@ -7082,7 +7082,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con { Powers PowerType = Powers(GetMiscValue()); - if (!caster || !caster->IsAlive() || !target->IsAlive() || target->getPowerType() != PowerType) + if (!caster || !caster->IsAlive() || !target->IsAlive() || !target->HasActivePowerType(PowerType)) return; if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo())) @@ -7189,7 +7189,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons { Powers PowerType = Powers(GetMiscValue()); - if (target->GetTypeId() == TYPEID_PLAYER && target->getPowerType() != PowerType && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) + if (target->GetTypeId() == TYPEID_PLAYER && !target->HasActivePowerType(PowerType) && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) return; if (!target->IsAlive() || !target->GetMaxPower(PowerType)) @@ -7223,7 +7223,7 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con { Powers PowerType = Powers(GetMiscValue()); - if (!caster || !target->IsAlive() || target->getPowerType() != PowerType) + if (!caster || !target->IsAlive() || !target->HasActivePowerType(PowerType)) return; if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo())) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 0ea3ccb2c..75522d2dd 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -6173,7 +6173,7 @@ SpellCastResult Spell::CheckCast(bool strict) // Can be area effect, Check only for players and not check if target - caster (spell can have multiply drain/burn effects) if (m_caster->GetTypeId() == TYPEID_PLAYER) if (Unit* target = m_targets.GetUnitTarget()) - if (target != m_caster && target->getPowerType() != Powers(m_spellInfo->Effects[i].MiscValue)) + if (target != m_caster && !target->HasActivePowerType(Powers(m_spellInfo->Effects[i].MiscValue))) return SPELL_FAILED_BAD_TARGETS; break; } @@ -6702,7 +6702,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (!m_targets.GetUnitTarget()) return SPELL_FAILED_BAD_IMPLICIT_TARGETS; - if (m_targets.GetUnitTarget()->getPowerType() != POWER_MANA) + if (!m_targets.GetUnitTarget()->HasActivePowerType(POWER_MANA)) return SPELL_FAILED_BAD_TARGETS; break; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 98cd74e6c..aaeb85080 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1345,7 +1345,7 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex) Powers PowerType = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (!unitTarget || !unitTarget->IsAlive() || unitTarget->getPowerType() != PowerType || damage < 0) + if (!unitTarget || !unitTarget->IsAlive() || !unitTarget->HasActivePowerType(PowerType) || damage < 0) return; // add spell damage bonus @@ -1424,7 +1424,7 @@ void Spell::EffectPowerBurn(SpellEffIndex effIndex) Powers PowerType = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (!unitTarget || !unitTarget->IsAlive() || unitTarget->getPowerType() != PowerType || damage < 0) + if (!unitTarget || !unitTarget->IsAlive() || !unitTarget->HasActivePowerType(PowerType) || damage < 0) return; // burn x% of target's mana, up to maximum of 2x% of caster's mana (Mana Burn) @@ -1877,7 +1877,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && m_spellInfo->SpellFamilyName != SPELLFAMILY_POTION + if (unitTarget->GetTypeId() == TYPEID_PLAYER && !unitTarget->HasActivePowerType(power) && m_spellInfo->SpellFamilyName != SPELLFAMILY_POTION && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) return; @@ -1982,7 +1982,7 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) + if (unitTarget->GetTypeId() == TYPEID_PLAYER && !unitTarget->HasActivePowerType(power) && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) return; uint32 maxPower = unitTarget->GetMaxPower(power); diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 6feb5f6c3..da7c3e997 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -576,7 +576,7 @@ class spell_item_skull_of_impeding_doom : public AuraScript void CalculateManaLeechAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) { - if (!GetCaster() || GetCaster()->getPowerType() != POWER_MANA) + if (!GetCaster() || !GetCaster()->HasActivePowerType(POWER_MANA)) return; amount = GetCaster()->GetMaxPower(POWER_MANA) * 0.12f; // 5 ticks which reduce health by 60% diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 0cd83b638..4793489fe 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -483,7 +483,7 @@ class spell_pal_blessing_of_sanctuary : public AuraScript bool CheckProc(ProcEventInfo& /*eventInfo*/) { - return GetTarget()->getPowerType() == POWER_MANA; + return GetTarget()->HasActivePowerType(POWER_MANA); } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) @@ -969,7 +969,7 @@ class spell_pal_lay_on_hands : public SpellScript // Xinef: Glyph of Divinity if (Unit* target = GetExplTargetUnit()) - if (target->getPowerType() == POWER_MANA) + if (target->HasActivePowerType(POWER_MANA)) _manaAmount = target->GetPower(POWER_MANA); return SPELL_CAST_OK; diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 9fd557adb..5ca5f32b2 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -992,7 +992,7 @@ class spell_sha_mana_spring_totem : public SpellScript int32 damage = GetEffectValue(); if (Unit* target = GetHitUnit()) if (Unit* caster = GetCaster()) - if (target->getPowerType() == POWER_MANA) + if (target->HasActivePowerType(POWER_MANA)) caster->CastCustomSpell(target, SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE, &damage, 0, 0, true, 0, 0, GetOriginalCaster()->GetGUID()); } @@ -1017,7 +1017,7 @@ class spell_sha_mana_tide_totem : public SpellScript if (Unit* caster = GetCaster()) if (Unit* unitTarget = GetHitUnit()) { - if (unitTarget->getPowerType() == POWER_MANA) + if (unitTarget->HasActivePowerType(POWER_MANA)) { int32 effValue = GetEffectValue(); // Glyph of Mana Tide From c47c945aa4200829fbe1c6b11a2cc028d3420b9b Mon Sep 17 00:00:00 2001 From: sudlud Date: Fri, 9 Feb 2024 17:24:57 +0100 Subject: [PATCH 12/17] fix(Conf): disable LeaveGroupOnLogout by default (#18245) --- src/server/apps/worldserver/worldserver.conf.dist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 6ec3e9c9f..85a553875 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -3069,9 +3069,9 @@ Rate.RepairCost = 1 # Description: Should the player leave their group when they log out? # (It does not affect raids or dungeon finder groups) # -# Default: 1 - (Enabled) +# Default: 0 - (Disabled) -LeaveGroupOnLogout.Enabled = 1 +LeaveGroupOnLogout.Enabled = 0 # # Group.Raid.LevelRestriction From df33a57b78083b985905bc99baf573cd7fc812b9 Mon Sep 17 00:00:00 2001 From: Nathan Handley Date: Sat, 10 Feb 2024 09:25:00 -0600 Subject: [PATCH 13/17] feat(Core/Unit): New helper IsClass and script hook OnPlayerIsClass (#18243) * Class Comparison Logic Encapsulation - Parity * Add Context to IsClass * Add Unit IsClass script hook * Replace additional getClass with IsClass * Update CanUseItem to replace getClass with IsClass * Add separate context for pet vs ability * Change Create to Init since not all referenced contexts are creation * Align spacing in ClassContext * Drop context on LFGManager max power * Update IsClass context that wraps around Missle Barrage * Rename context for swapping weapons * Be more specific than CLASS_CONTEXT_TALENT * Remove duplicate context * Moved IsClass Hook to Player * Removed unused parameter in virtual base function * Added maybe_unused to IsClass virtual in order to compile To match the override signature, the virtual base needs to include the parameter in question, so using [maybe_unused] to signal to the compiler to allow it * Remove extra blank line * Add ABILITY_REACTIVE context * Add context for PET_CHARM * Remove explicit nullopt check per review * Code Readability - Change if to if else in pet Due to the return pattern, this doesn't change functionality in any way * Add OnPlayer to disambiguate --------- Co-authored-by: NathanHandley --- .../game/ArenaSpectator/ArenaSpectator.cpp | 2 +- src/server/game/DungeonFinding/LFGMgr.cpp | 2 +- .../game/Entities/Creature/Creature.cpp | 6 +- src/server/game/Entities/Pet/Pet.cpp | 35 ++- src/server/game/Entities/Player/Player.cpp | 51 +++-- src/server/game/Entities/Player/Player.h | 2 + .../game/Entities/Player/PlayerGossip.cpp | 2 +- .../game/Entities/Player/PlayerStorage.cpp | 98 ++++----- src/server/game/Entities/Unit/StatSystem.cpp | 201 +++++++++--------- src/server/game/Entities/Unit/Unit.cpp | 30 +-- src/server/game/Entities/Unit/Unit.h | 24 +++ src/server/game/Entities/Vehicle/Vehicle.cpp | 2 +- src/server/game/Groups/Group.cpp | 2 +- .../game/Handlers/BattleGroundHandler.cpp | 2 +- src/server/game/Handlers/LootHandler.cpp | 6 +- src/server/game/Misc/GameGraveyard.cpp | 2 +- .../Scripting/ScriptDefines/PlayerScript.cpp | 13 ++ .../Scripting/ScriptDefines/PlayerScript.h | 2 + src/server/game/Scripting/ScriptMgr.h | 1 + src/server/game/Scripting/ScriptObjectFwd.h | 1 + .../game/Spells/Auras/SpellAuraEffects.cpp | 8 +- src/server/game/Spells/Auras/SpellAuras.cpp | 2 +- src/server/game/Spells/Spell.cpp | 8 +- src/server/game/Spells/SpellEffects.cpp | 8 +- src/server/scripts/Commands/cs_reset.cpp | 2 +- .../BlackwingLair/boss_nefarian.cpp | 2 +- .../EasternKingdoms/ZulAman/boss_hexlord.cpp | 4 +- .../scripts/Kalimdor/zone_moonglade.cpp | 75 ++++--- .../IcecrownCitadel/boss_sindragosa.cpp | 6 +- .../IcecrownCitadel/icecrown_citadel.cpp | 5 +- src/server/scripts/Spells/spell_dk.cpp | 4 +- src/server/scripts/Spells/spell_generic.cpp | 14 +- src/server/scripts/Spells/spell_item.cpp | 2 +- 33 files changed, 344 insertions(+), 280 deletions(-) diff --git a/src/server/game/ArenaSpectator/ArenaSpectator.cpp b/src/server/game/ArenaSpectator/ArenaSpectator.cpp index 6048b7636..0f5e11c35 100644 --- a/src/server/game/ArenaSpectator/ArenaSpectator.cpp +++ b/src/server/game/ArenaSpectator/ArenaSpectator.cpp @@ -42,7 +42,7 @@ bool ArenaSpectator::HandleSpectatorSpectateCommand(ChatHandler* handler, std::s return true; } - if (player->getClass() == CLASS_DEATH_KNIGHT && player->GetMapId() == 609) + if (player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TELEPORT) && player->GetMapId() == 609) { handler->PSendSysMessage("Death Knights can't spectate before finishing questline."); return true; diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 3de529c90..d5a275812 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -1055,7 +1055,7 @@ namespace lfg baseAP = p->GetTotalAttackPowerValue(BASE_ATTACK); rangedAP = p->GetTotalAttackPowerValue(RANGED_ATTACK); maxPower = 0; - if (p->getClass() == CLASS_DRUID) + if (p->IsClass(CLASS_DRUID)) maxPower = p->GetMaxPower(POWER_MANA); else maxPower = (p->getPowerType() == POWER_RAGE || p->getPowerType() == POWER_RUNIC_POWER) ? p->GetMaxPower(p->getPowerType()) / 10 : p->GetMaxPower(p->getPowerType()); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index e71ef8759..5c0260411 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1196,8 +1196,8 @@ bool Creature::isCanInteractWithBattleMaster(Player* player, bool msg) const bool Creature::isCanTrainingAndResetTalentsOf(Player* player) const { return player->GetLevel() >= 10 - && GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS - && player->getClass() == GetCreatureTemplate()->trainer_class; + && GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS + && player->IsClass((Classes)GetCreatureTemplate()->trainer_class, CLASS_CONTEXT_CLASS_TRAINER); } bool Creature::IsValidTrainerForPlayer(Player* player, uint32* npcFlags /*= nullptr*/) const @@ -1211,7 +1211,7 @@ bool Creature::IsValidTrainerForPlayer(Player* player, uint32* npcFlags /*= null { case TRAINER_TYPE_CLASS: case TRAINER_TYPE_PETS: - if (m_creatureInfo->trainer_class && m_creatureInfo->trainer_class != player->getClass()) + if (m_creatureInfo->trainer_class && !player->IsClass((Classes)m_creatureInfo->trainer_class, CLASS_CONTEXT_CLASS_TRAINER)) { if (npcFlags) *npcFlags &= ~UNIT_NPC_FLAG_TRAINER_CLASS; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index f58797ac4..416bb2d0b 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -107,7 +107,7 @@ void Pet::AddToWorld() { if (Player* owner = GetOwner()) { - if (getPetType() == SUMMON_PET && owner->getClass() == CLASS_WARLOCK) + if (getPetType() == SUMMON_PET && owner->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET)) { owner->SetLastPetSpell(GetUInt32Value(UNIT_CREATED_BY_SPELL)); } @@ -238,7 +238,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c bool forceLoadFromDB = false; sScriptMgr->OnBeforeLoadPetFromDB(owner, petEntry, petnumber, current, forceLoadFromDB); - if (!forceLoadFromDB && (owner->getClass() == CLASS_DEATH_KNIGHT && !owner->CanSeeDKPet())) // DK Pet exception + if (!forceLoadFromDB && (owner->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_PET) && !owner->CanSeeDKPet())) // DK Pet exception return false; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petInfo->CreatedBySpellId); @@ -1043,12 +1043,12 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) if (petType == MAX_PET_TYPE) { // The petType was not overwritten by the hook, continue with default initialization - if (owner->getClass() == CLASS_WARLOCK || - owner->getClass() == CLASS_SHAMAN || // Fire Elemental - owner->getClass() == CLASS_DEATH_KNIGHT || // Risen Ghoul - owner->getClass() == CLASS_MAGE) // Water Elemental with glyph + if (owner->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET) || + owner->IsClass(CLASS_SHAMAN, CLASS_CONTEXT_PET) || // Fire Elemental + owner->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_PET) || // Risen Ghoul + owner->IsClass(CLASS_MAGE, CLASS_CONTEXT_PET)) // Water Elemental with glyph petType = SUMMON_PET; - else if (owner->getClass() == CLASS_HUNTER) + else if (owner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) { petType = HUNTER_PET; } @@ -1080,7 +1080,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel * 50)); uint32 attackTime = BASE_ATTACK_TIME; - if (owner->getClass() != CLASS_HUNTER && cinfo->BaseAttackTime >= 1000) + if (!owner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET) && cinfo->BaseAttackTime >= 1000) attackTime = cinfo->BaseAttackTime; SetAttackTime(BASE_ATTACK, attackTime); @@ -2299,17 +2299,14 @@ bool Pet::IsPermanentPetFor(Player* owner) const switch (getPetType()) { case SUMMON_PET: - switch (owner->getClass()) - { - case CLASS_WARLOCK: - return GetCreatureTemplate()->type == CREATURE_TYPE_DEMON; - case CLASS_DEATH_KNIGHT: - return GetCreatureTemplate()->type == CREATURE_TYPE_UNDEAD; - case CLASS_MAGE: - return GetEntry() == 37994; - default: - return false; - } + if (owner->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET)) + return GetCreatureTemplate()->type == CREATURE_TYPE_DEMON; + else if (owner->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_PET)) + return GetCreatureTemplate()->type == CREATURE_TYPE_UNDEAD; + else if (owner->IsClass(CLASS_MAGE, CLASS_CONTEXT_PET)) + return GetEntry() == 37994; + else + return false; case HUNTER_PET: return true; default: diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index d4736d5f7..d7dbf6817 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -553,7 +553,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0); // set starting level - uint32 start_level = getClass() != CLASS_DEATH_KNIGHT + uint32 start_level = !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) ? sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); @@ -568,7 +568,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo InitRunes(); - SetUInt32Value(PLAYER_FIELD_COINAGE, getClass() != CLASS_DEATH_KNIGHT + SetUInt32Value(PLAYER_FIELD_COINAGE, !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) ? sWorld->getIntConfig(CONFIG_START_PLAYER_MONEY) : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_MONEY)); SetHonorPoints(sWorld->getIntConfig(CONFIG_START_HONOR_POINTS)); @@ -633,7 +633,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo switch (iProto->Spells[0].SpellCategory) { case SPELL_CATEGORY_FOOD: // food - count = getClass() == CLASS_DEATH_KNIGHT ? 10 : 4; + count = IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) ? 10 : 4; break; case SPELL_CATEGORY_DRINK: // drink count = 2; @@ -1269,6 +1269,15 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) return true; } +bool Player::IsClass(Classes unitClass, ClassContext context) const +{ + Optional scriptResult = sScriptMgr->OnPlayerIsClass(this, unitClass, context); + if (scriptResult != std::nullopt) + return *scriptResult; + else + return (getClass() == unitClass); +} + void Player::ToggleAFK() { ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK); @@ -1460,7 +1469,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati } else { - if (getClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977)) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TELEPORT) && GetMapId() == 609 && !IsGameMaster() && !HasSpell(50977)) { SendTransferAborted(mapid, TRANSFER_ABORT_UNIQUE_MESSAGE, 1); return false; @@ -1745,7 +1754,7 @@ void Player::RegenerateAll() Regenerate(POWER_MANA); // Runes act as cooldowns, and they don't need to send any data - if (getClass() == CLASS_DEATH_KNIGHT) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) for (uint8 i = 0; i < MAX_RUNES; ++i) { // xinef: implement grace @@ -1775,7 +1784,7 @@ void Player::RegenerateAll() } Regenerate(POWER_RAGE); - if (getClass() == CLASS_DEATH_KNIGHT) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) Regenerate(POWER_RUNIC_POWER); m_regenTimerCount -= 2000; @@ -2106,7 +2115,7 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask) // pussywizard: many npcs have missing conditions for class training and rogue trainer can for eg. train dual wield to a shaman :/ too many to change in sql and watch in the future // pussywizard: this function is not used when talking, but when already taking action (buy spell, reset talents, show spell list) - if (npcflagmask & (UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS) && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS && getClass() != creature->GetCreatureTemplate()->trainer_class) + if (npcflagmask & (UNIT_NPC_FLAG_TRAINER | UNIT_NPC_FLAG_TRAINER_CLASS) && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS && !IsClass((Classes)creature->GetCreatureTemplate()->trainer_class, CLASS_CONTEXT_CLASS_TRAINER)) return nullptr; return creature; @@ -6393,7 +6402,7 @@ void Player::DuelComplete(DuelCompleteType type) opponent->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1); // Credit for quest Death's Challenge - if (getClass() == CLASS_DEATH_KNIGHT && opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_QUEST) && opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE) { opponent->CastSpell(opponent, 52994, true); } @@ -6789,7 +6798,7 @@ void Player::_ApplyItemBonuses(ItemTemplate const* proto, uint8 slot, bool apply } // Druids get feral AP bonus from weapon dps (also use DPS from ScalingStatValue) - if (getClass() == CLASS_DRUID) + if (IsClass(CLASS_DRUID, CLASS_CONTEXT_STATS)) { int32 dpsMod = 0; int32 feral_bonus = 0; @@ -9846,7 +9855,7 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply) if (apply) { m_spellMods[mod->op].push_back(mod); - if (getClass() == CLASS_MAGE) + if (IsClass(CLASS_MAGE, CLASS_CONTEXT_ABILITY)) m_spellMods[mod->op].sort(MageSpellModPred()); else m_spellMods[mod->op].sort(SpellModPred()); @@ -10280,7 +10289,7 @@ bool Player::ActivateTaxiPathTo(std::vector const& nodes, Creature* npc // only one mount ID for both sides. Probably not good to use 315 in case DBC nodes // change but I couldn't find a suitable alternative. OK to use class because only DK // can use this taxi. - uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetTeamId(true), npc == nullptr || (sourcenode == 315 && getClass() == CLASS_DEATH_KNIGHT)); + uint32 mount_display_id = sObjectMgr->GetTaxiMountDisplayId(sourcenode, GetTeamId(true), npc == nullptr || (sourcenode == 315 && IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TAXI))); // in spell case allow 0 model if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0) @@ -11329,7 +11338,7 @@ WorldLocation Player::GetStartPosition() const { PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(true), getClass()); uint32 mapId = info->mapId; - if (getClass() == CLASS_DEATH_KNIGHT && HasSpell(50977)) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) && HasSpell(50977)) return WorldLocation(0, 2352.0f, -5709.0f, 154.5f, 0.0f); return WorldLocation(mapId, info->positionX, info->positionY, info->positionZ, 0); } @@ -11823,7 +11832,7 @@ void Player::LearnDefaultSkill(uint32 skillId, uint16 rank) { skillValue = maxValue; } - else if (getClass() == CLASS_DEATH_KNIGHT) + else if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_SKILL)) { skillValue = std::min(std::max({ 1, uint16((GetLevel() - 1) * 5) }), maxValue); } @@ -11856,7 +11865,7 @@ void Player::LearnDefaultSkill(uint32 skillId, uint16 rank) { skillValue = maxValue; } - else if (getClass() == CLASS_DEATH_KNIGHT) + else if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_SKILL)) { skillValue = std::min(std::max({ uint16(1), uint16((GetLevel() - 1) * 5) }), maxValue); } @@ -13381,7 +13390,7 @@ static RuneType runeSlotTypes[MAX_RUNES] = void Player::InitRunes() { - if (getClass() != CLASS_DEATH_KNIGHT) + if (!IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; m_runes = new Runes; @@ -13547,7 +13556,7 @@ uint32 Player::CalculateTalentsPoints() const uint32 base_talent = GetLevel() < 10 ? 0 : GetLevel() - 9; uint32 talentPointsForLevel = 0; - if (getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609) + if (!IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TALENT_POINT_CALC) || GetMapId() != 609) { talentPointsForLevel = base_talent; } @@ -14179,19 +14188,21 @@ void Player::ResummonPetTemporaryUnSummonedIfAny() bool Player::CanResummonPet(uint32 spellid) { - if (getClass() == CLASS_DEATH_KNIGHT) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_PET)) { if (CanSeeDKPet()) return true; else if (spellid == 52150) // Raise Dead return false; } - else if (getClass() == CLASS_MAGE) + + if (IsClass(CLASS_MAGE, CLASS_CONTEXT_PET)) { if (HasSpell(31687) && HasAura(70937)) //Has [Summon Water Elemental] spell and [Glyph of Eternal Water]. return true; } - else if (getClass() == CLASS_HUNTER) + + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) { return true; } @@ -15176,7 +15187,7 @@ void Player::ActivateSpec(uint8 spec) AutoUnequipOffhandIfNeed(); // Xinef: Patch 3.2.0: Switching spec removes paladins spell Righteous Fury (25780) - if (getClass() == CLASS_PALADIN) + if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_ABILITY)) RemoveAurasDueToSpell(25780); // Xinef: Remove talented single target auras at other targets diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index bb99778be..0358d14f6 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1112,6 +1112,8 @@ public: static bool BuildEnumData(PreparedQueryResult result, WorldPacket* data); + [[nodiscard]] bool IsClass(Classes playerClass, ClassContext context = CLASS_CONTEXT_NONE) const override; + void SetInWater(bool apply); [[nodiscard]] bool IsInWater() const override { return m_isInWater; } diff --git a/src/server/game/Entities/Player/PlayerGossip.cpp b/src/server/game/Entities/Player/PlayerGossip.cpp index 77bc1901a..953adca83 100644 --- a/src/server/game/Entities/Player/PlayerGossip.cpp +++ b/src/server/game/Entities/Player/PlayerGossip.cpp @@ -109,7 +109,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool canTalk = false; break; case GOSSIP_OPTION_STABLEPET: - if (getClass() != CLASS_HUNTER) + if (!IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) canTalk = false; break; case GOSSIP_OPTION_QUESTGIVER: diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 1aacad7b7..da9c0e338 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -133,8 +133,6 @@ void Player::SetSheath(SheathState sheathed) uint8 Player::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) const { - uint8 playerClass = getClass(); - uint8 slots[4]; slots[0] = NULL_SLOT; slots[1] = NULL_SLOT; @@ -246,23 +244,23 @@ uint8 Player::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) c switch (proto->SubClass) { case ITEM_SUBCLASS_ARMOR_LIBRAM: - if (playerClass == CLASS_PALADIN) + if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; case ITEM_SUBCLASS_ARMOR_IDOL: - if (playerClass == CLASS_DRUID) + if (IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; case ITEM_SUBCLASS_ARMOR_TOTEM: - if (playerClass == CLASS_SHAMAN) + if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; case ITEM_SUBCLASS_ARMOR_MISC: - if (playerClass == CLASS_WARLOCK) + if (IsClass(CLASS_WARLOCK, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; case ITEM_SUBCLASS_ARMOR_SIGIL: - if (playerClass == CLASS_DEATH_KNIGHT) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; } @@ -2270,16 +2268,13 @@ InventoryResult Player::CanUseItem(Item* pItem, bool not_loading) const // In fact it's a visual bug, everything works properly... I need sniffs of operations with // binded to account items from off server. - switch (getClass()) + if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) { - case CLASS_HUNTER: - case CLASS_SHAMAN: - allowEquip = (itemSkill == SKILL_MAIL); - break; - case CLASS_PALADIN: - case CLASS_WARRIOR: - allowEquip = (itemSkill == SKILL_PLATE_MAIL); - break; + allowEquip = (itemSkill == SKILL_PLATE_MAIL); + } + else if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + allowEquip = (itemSkill == SKILL_MAIL); } } if (!allowEquip && GetSkillValue(itemSkill) == 0) @@ -2394,39 +2389,40 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje return EQUIP_ERR_CANT_EQUIP_SKILL; } - uint8 _class = getClass(); - if (proto->Class == ITEM_CLASS_WEAPON && GetSkillValue(item_weapon_skills[proto->SubClass]) == 0) return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; if (proto->Class == ITEM_CLASS_ARMOR) { // Check for shields - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD && !(_class == CLASS_PALADIN || _class == CLASS_WARRIOR || _class == CLASS_SHAMAN)) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD && !( + IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_SHIELDS) + || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_SHIELDS) + || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_SHIELDS))) { return EQUIP_ERR_NO_REQUIRED_PROFICIENCY; } // Check for librams. - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_LIBRAM && _class != CLASS_PALADIN) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_LIBRAM && !IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_RELIC)) { return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; } // CHeck for idols. - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_IDOL && _class != CLASS_DRUID) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_IDOL && !IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_RELIC)) { return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; } // Check for totems. - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_TOTEM && _class != CLASS_SHAMAN) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_TOTEM && !IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_RELIC)) { return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; } // Check for sigils. - if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SIGIL && _class != CLASS_DEATH_KNIGHT) + if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SIGIL && !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_RELIC)) { return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM; } @@ -2436,33 +2432,33 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje proto->InventoryType != INVTYPE_CLOAK) { uint32 subclassToCompare = ITEM_SUBCLASS_ARMOR_CLOTH; - switch (_class) + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) { - case CLASS_WARRIOR: - if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat()) - { - return EQUIP_ERR_CANT_DO_RIGHT_NOW; - } - [[fallthrough]]; - case CLASS_DEATH_KNIGHT: - case CLASS_PALADIN: - subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE; - break; - case CLASS_HUNTER: - case CLASS_SHAMAN: - subclassToCompare = ITEM_SUBCLASS_ARMOR_MAIL; - break; - case CLASS_ROGUE: - if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat()) - { - return EQUIP_ERR_CANT_DO_RIGHT_NOW; - } - [[fallthrough]]; - case CLASS_DRUID: - subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER; - break; - default: - break; + subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE; + } + else if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + if ((proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat())) + { + return EQUIP_ERR_CANT_DO_RIGHT_NOW; + } + subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE; + } + else if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + subclassToCompare = ITEM_SUBCLASS_ARMOR_MAIL; + } + else if (IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER; + } + else if (IsClass(CLASS_ROGUE, CLASS_CONTEXT_EQUIP_ARMOR_CLASS)) + { + if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat()) + { + return EQUIP_ERR_CANT_DO_RIGHT_NOW; + } + subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER; } if (proto->SubClass > subclassToCompare) @@ -2772,7 +2768,7 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) if (pProto && IsInCombat() && (pProto->Class == ITEM_CLASS_WEAPON || pProto->InventoryType == INVTYPE_RELIC) && m_weaponChangeTimer == 0) { - uint32 cooldownSpell = getClass() == CLASS_ROGUE ? 6123 : 6119; + uint32 cooldownSpell = IsClass(CLASS_ROGUE, CLASS_CONTEXT_WEAPON_SWAP) ? 6123 : 6119; SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(cooldownSpell); if (!spellProto) @@ -4645,7 +4641,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool } case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon { - if (getClass() == CLASS_SHAMAN) + if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_ABILITY)) { float addValue = 0.0f; if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND) diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 5e23e2142..f2191b39e 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -346,134 +346,127 @@ void Player::UpdateAttackPowerAndDamage(bool ranged) index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS; index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER; - switch (getClass()) + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS)) { - case CLASS_HUNTER: - val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; - break; - case CLASS_ROGUE: - case CLASS_WARRIOR: - val2 = level + GetStat(STAT_AGILITY) - 10.0f; - break; - case CLASS_DRUID: - switch (GetShapeshiftForm()) - { - case FORM_CAT: - case FORM_BEAR: - case FORM_DIREBEAR: - val2 = 0.0f; - break; - default: - val2 = GetStat(STAT_AGILITY) - 10.0f; - break; - } + val2 = level * 2.0f + GetStat(STAT_AGILITY) - 10.0f; + } + else if (IsClass(CLASS_ROGUE, CLASS_CONTEXT_STATS) || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_STATS)) + { + val2 = level + GetStat(STAT_AGILITY) - 10.0f; + } + else if (IsClass(CLASS_DRUID, CLASS_CONTEXT_STATS)) + { + switch (GetShapeshiftForm()) + { + case FORM_CAT: + case FORM_BEAR: + case FORM_DIREBEAR: + val2 = 0.0f; break; default: val2 = GetStat(STAT_AGILITY) - 10.0f; break; + } + } + else + { + val2 = GetStat(STAT_AGILITY) - 10.0f; } } else { - switch (getClass()) + if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_STATS) || IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_STATS) || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_STATS)) { - case CLASS_PALADIN: - case CLASS_DEATH_KNIGHT: - case CLASS_WARRIOR: - val2 = level * 3.0f + GetStat(STAT_STRENGTH) * 2.0f - 20.0f; - break; - case CLASS_HUNTER: - case CLASS_SHAMAN: - case CLASS_ROGUE: - val2 = level * 2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; - break; - case CLASS_DRUID: + val2 = level * 3.0f + GetStat(STAT_STRENGTH) * 2.0f - 20.0f; + } + else if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS) || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_STATS) || IsClass(CLASS_ROGUE, CLASS_CONTEXT_STATS)) + { + val2 = level * 2.0f + GetStat(STAT_STRENGTH) + GetStat(STAT_AGILITY) - 20.0f; + } + else if (IsClass(CLASS_DRUID, CLASS_CONTEXT_STATS)) + { + // Check if Predatory Strikes is skilled + float mLevelMult = 0.0f; + float weapon_bonus = 0.0f; + if (IsInFeralForm()) + { + Unit::AuraEffectList const& mDummy = GetAuraEffectsByType(SPELL_AURA_DUMMY); + for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) { - // Check if Predatory Strikes is skilled - float mLevelMult = 0.0f; - float weapon_bonus = 0.0f; - if (IsInFeralForm()) + AuraEffect* aurEff = *itr; + if (aurEff->GetSpellInfo()->SpellIconID == 1563) { - Unit::AuraEffectList const& mDummy = GetAuraEffectsByType(SPELL_AURA_DUMMY); - for (Unit::AuraEffectList::const_iterator itr = mDummy.begin(); itr != mDummy.end(); ++itr) + switch (aurEff->GetEffIndex()) { - AuraEffect* aurEff = *itr; - if (aurEff->GetSpellInfo()->SpellIconID == 1563) + case 0: // Predatory Strikes (effect 0) + mLevelMult = CalculatePct(1.0f, aurEff->GetAmount()); + break; + case 1: // Predatory Strikes (effect 1) + if (Item* mainHand = m_items[EQUIPMENT_SLOT_MAINHAND]) { - switch (aurEff->GetEffIndex()) + // also gains % attack power from equipped weapon + ItemTemplate const* proto = mainHand->GetTemplate(); + if (!proto) + continue; + + uint32 ap = proto->getFeralBonus(); + // Get AP Bonuses from weapon + for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) { - case 0: // Predatory Strikes (effect 0) - mLevelMult = CalculatePct(1.0f, aurEff->GetAmount()); + if (i >= proto->StatsCount) break; - case 1: // Predatory Strikes (effect 1) - if (Item* mainHand = m_items[EQUIPMENT_SLOT_MAINHAND]) - { - // also gains % attack power from equipped weapon - ItemTemplate const* proto = mainHand->GetTemplate(); - if (!proto) - continue; - uint32 ap = proto->getFeralBonus(); - // Get AP Bonuses from weapon - for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) - { - if (i >= proto->StatsCount) - break; - - if (proto->ItemStat[i].ItemStatType == ITEM_MOD_ATTACK_POWER) - ap += proto->ItemStat[i].ItemStatValue; - } - - // Get AP Bonuses from weapon spells - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - // no spell - if (!proto->Spells[i].SpellId || proto->Spells[i].SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP) - continue; - - // check if it is valid spell - SpellInfo const* spellproto = sSpellMgr->GetSpellInfo(proto->Spells[i].SpellId); - if (!spellproto) - continue; - - for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) - if (spellproto->Effects[j].ApplyAuraName == SPELL_AURA_MOD_ATTACK_POWER) - ap += spellproto->Effects[j].CalcValue(); - } - - weapon_bonus = CalculatePct(float(ap), aurEff->GetAmount()); - } - break; - default: - break; + if (proto->ItemStat[i].ItemStatType == ITEM_MOD_ATTACK_POWER) + ap += proto->ItemStat[i].ItemStatValue; } - } - } - } - switch (GetShapeshiftForm()) - { - case FORM_CAT: - val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f + GetStat(STAT_AGILITY) - 20.0f + weapon_bonus + m_baseFeralAP; - break; - case FORM_BEAR: - case FORM_DIREBEAR: - val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + weapon_bonus + m_baseFeralAP; - break; - case FORM_MOONKIN: - val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + m_baseFeralAP; + // Get AP Bonuses from weapon spells + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + // no spell + if (!proto->Spells[i].SpellId || proto->Spells[i].SpellTrigger != ITEM_SPELLTRIGGER_ON_EQUIP) + continue; + + // check if it is valid spell + SpellInfo const* spellproto = sSpellMgr->GetSpellInfo(proto->Spells[i].SpellId); + if (!spellproto) + continue; + + for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) + if (spellproto->Effects[j].ApplyAuraName == SPELL_AURA_MOD_ATTACK_POWER) + ap += spellproto->Effects[j].CalcValue(); + } + + weapon_bonus = CalculatePct(float(ap), aurEff->GetAmount()); + } break; default: - val2 = GetStat(STAT_STRENGTH) * 2.0f - 20.0f; break; + } } - break; } - case CLASS_MAGE: - case CLASS_PRIEST: - case CLASS_WARLOCK: - val2 = GetStat(STAT_STRENGTH) - 10.0f; + } + + switch (GetShapeshiftForm()) + { + case FORM_CAT: + val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f + GetStat(STAT_AGILITY) - 20.0f + weapon_bonus + m_baseFeralAP; break; + case FORM_BEAR: + case FORM_DIREBEAR: + val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + weapon_bonus + m_baseFeralAP; + break; + case FORM_MOONKIN: + val2 = (GetLevel() * mLevelMult) + GetStat(STAT_STRENGTH) * 2.0f - 20.0f + m_baseFeralAP; + break; + default: + val2 = GetStat(STAT_STRENGTH) * 2.0f - 20.0f; + break; + } + } + else if (IsClass(CLASS_MAGE, CLASS_CONTEXT_STATS) || IsClass(CLASS_PRIEST, CLASS_CONTEXT_STATS) || IsClass(CLASS_WARLOCK, CLASS_CONTEXT_STATS)) + { + val2 = GetStat(STAT_STRENGTH) - 10.0f; } } @@ -521,7 +514,7 @@ void Player::UpdateAttackPowerAndDamage(bool ranged) UpdateDamagePhysical(BASE_ATTACK); if (CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon UpdateDamagePhysical(OFF_ATTACK); - if (getClass() == CLASS_SHAMAN || getClass() == CLASS_PALADIN) // mental quickness + if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_STATS) || IsClass(CLASS_PALADIN, CLASS_CONTEXT_STATS)) // mental quickness UpdateSpellDamageAndHealingBonus(); } } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index b4c6812af..f7576fbdf 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -8834,7 +8834,7 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp // Convert recently used Blood Rune to Death Rune if (Player* player = ToPlayer()) { - if (player->getClass() != CLASS_DEATH_KNIGHT) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return false; // xinef: not true @@ -9565,7 +9565,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg // Item - Death Knight T10 Melee 4P Bonus if (auraSpellInfo->Id == 70656) { - if (GetTypeId() != TYPEID_PLAYER || getClass() != CLASS_DEATH_KNIGHT) + if (GetTypeId() != TYPEID_PLAYER || !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return false; for (uint8 i = 0; i < MAX_RUNES; ++i) @@ -9576,7 +9576,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg else if (auraSpellInfo->SpellIconID == 85) { Player* plr = ToPlayer(); - if (!plr || plr->getClass() != CLASS_DEATH_KNIGHT || !procSpell) + if (!plr || !plr->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY) || !procSpell) return false; if (!plr->IsBaseRuneSlotsOnCooldown(RUNE_BLOOD)) @@ -13823,7 +13823,7 @@ void Unit::ClearInCombat() else if (Player* player = ToPlayer()) { player->UpdatePotionCooldown(); - if (player->getClass() == CLASS_DEATH_KNIGHT) + if (player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) for (uint8 i = 0; i < MAX_RUNES; ++i) player->SetGracePeriod(i, 0); } @@ -16361,7 +16361,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (procExtra & PROC_EX_DODGE) { // Update AURA_STATE on dodge - if (getClass() != CLASS_ROGUE) // skip Rogue Riposte + if (!IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY_REACTIVE)) // skip Rogue Riposte { ModifyAuraState(AURA_STATE_DEFENSE, true); StartReactiveTimer(REACTIVE_DEFENSE); @@ -16371,7 +16371,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (procExtra & PROC_EX_PARRY) { // For Hunters only Counterattack (skip Mongoose bite) - if (getClass() == CLASS_HUNTER) + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_ABILITY_REACTIVE)) { ModifyAuraState(AURA_STATE_HUNTER_PARRY, true); StartReactiveTimer(REACTIVE_HUNTER_PARRY); @@ -16394,7 +16394,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u // Overpower on victim dodge if (procExtra & PROC_EX_DODGE) { - if (getClass() == CLASS_WARRIOR) + if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_ABILITY_REACTIVE)) { AddComboPoints(target, 1); StartReactiveTimer(REACTIVE_OVERPOWER); @@ -17201,9 +17201,9 @@ void Unit::ClearAllReactives() if (HasAuraState(AURA_STATE_DEFENSE)) ModifyAuraState(AURA_STATE_DEFENSE, false); - if (getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY)) + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_ABILITY_REACTIVE) && HasAuraState(AURA_STATE_HUNTER_PARRY)) ModifyAuraState(AURA_STATE_HUNTER_PARRY, false); - if (getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER) + if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_ABILITY_REACTIVE) && GetTypeId() == TYPEID_PLAYER) ClearComboPoints(); } @@ -17227,11 +17227,11 @@ void Unit::UpdateReactives(uint32 p_time) ModifyAuraState(AURA_STATE_DEFENSE, false); break; case REACTIVE_HUNTER_PARRY: - if (getClass() == CLASS_HUNTER && HasAuraState(AURA_STATE_HUNTER_PARRY)) + if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_ABILITY_REACTIVE) && HasAuraState(AURA_STATE_HUNTER_PARRY)) ModifyAuraState(AURA_STATE_HUNTER_PARRY, false); break; case REACTIVE_OVERPOWER: - if (getClass() == CLASS_WARRIOR) + if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_ABILITY_REACTIVE)) { ClearComboPoints(); } @@ -18096,7 +18096,7 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp // Spirit of Redemption // if talent known but not triggered (check priest class for speedup check) bool spiritOfRedemption = false; - if (victim->GetTypeId() == TYPEID_PLAYER && victim->getClass() == CLASS_PRIEST && !victim->ToPlayer()->HasPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS)) + if (victim->GetTypeId() == TYPEID_PLAYER && victim->IsClass(CLASS_PRIEST, CLASS_CONTEXT_ABILITY) && !victim->ToPlayer()->HasPlayerFlag(PLAYER_FLAGS_IS_OUT_OF_BOUNDS)) { if (AuraEffect* aurEff = victim->GetAuraEffectDummy(20711)) { @@ -18704,7 +18704,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au GetMotionMaster()->MoveIdle(); StopMoving(); - if (charmer->GetTypeId() == TYPEID_PLAYER && charmer->getClass() == CLASS_WARLOCK && ToCreature()->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON) + if (charmer->GetTypeId() == TYPEID_PLAYER && charmer->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET_CHARM) && ToCreature()->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON) { // Disable CreatureAI/SmartAI and switch to CharmAI when charmed by warlock Creature* charmed = ToCreature(); @@ -18770,7 +18770,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au playerCharmer->PossessSpellInitialize(); break; case CHARM_TYPE_CHARM: - if (GetTypeId() == TYPEID_UNIT && charmer->getClass() == CLASS_WARLOCK) + if (GetTypeId() == TYPEID_UNIT && charmer->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET_CHARM)) { CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate(); if (cinfo && cinfo->type == CREATURE_TYPE_DEMON) @@ -18892,7 +18892,7 @@ void Unit::RemoveCharmedBy(Unit* charmer) ClearUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD); break; case CHARM_TYPE_CHARM: - if (GetTypeId() == TYPEID_UNIT && charmer->getClass() == CLASS_WARLOCK) + if (GetTypeId() == TYPEID_UNIT && charmer->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET_CHARM)) { CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate(); if (cinfo && cinfo->type == CREATURE_TYPE_DEMON) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index ad55d384c..b4e5cc699 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -440,6 +440,29 @@ enum DamageEffectType : uint8 SELF_DAMAGE = 5 }; +// Used for IsClass hook +enum ClassContext : uint8 +{ + CLASS_CONTEXT_NONE = 0, // Default + CLASS_CONTEXT_INIT = 1, + CLASS_CONTEXT_TELEPORT = 2, + CLASS_CONTEXT_QUEST = 3, + CLASS_CONTEXT_STATS = 4, + CLASS_CONTEXT_TAXI = 5, + CLASS_CONTEXT_SKILL = 6, + CLASS_CONTEXT_TALENT_POINT_CALC = 7, + CLASS_CONTEXT_ABILITY = 8, + CLASS_CONTEXT_ABILITY_REACTIVE = 9, + CLASS_CONTEXT_PET = 10, + CLASS_CONTEXT_PET_CHARM = 11, + CLASS_CONTEXT_EQUIP_RELIC = 12, + CLASS_CONTEXT_EQUIP_SHIELDS = 13, + CLASS_CONTEXT_EQUIP_ARMOR_CLASS = 14, + CLASS_CONTEXT_WEAPON_SWAP = 15, + CLASS_CONTEXT_GRAVEYARD = 16, + CLASS_CONTEXT_CLASS_TRAINER = 17 +}; + // Value masks for UNIT_FIELD_FLAGS // EnumUtils: DESCRIBE THIS enum UnitFlags : uint32 @@ -1436,6 +1459,7 @@ public: void setRace(uint8 race); [[nodiscard]] uint32 getRaceMask() const { return 1 << (getRace(true) - 1); } [[nodiscard]] uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); } + [[nodiscard]] virtual bool IsClass(Classes unitClass, [[maybe_unused]] ClassContext context = CLASS_CONTEXT_NONE) const { return (getClass() == unitClass); } [[nodiscard]] uint32 getClassMask() const { return 1 << (getClass() - 1); } [[nodiscard]] uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); } [[nodiscard]] DisplayRace GetDisplayRaceFromModelId(uint32 modelId) const; diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index b33502da2..1e7ecbe8b 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -72,7 +72,7 @@ void Vehicle::Install() { if (PowerDisplayEntry const* powerDisplay = sPowerDisplayStore.LookupEntry(_vehicleInfo->m_powerDisplayId)) _me->setPowerType(Powers(powerDisplay->PowerType)); - else if (_me->getClass() == CLASS_ROGUE) + else if (_me->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY)) _me->setPowerType(POWER_ENERGY); } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index e2208fa84..f6563042e 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1983,7 +1983,7 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* return ERR_IN_NON_RANDOM_BG; // don't let Death Knights join BG queues when they are not allowed to be teleported yet - if (member->getClass() == CLASS_DEATH_KNIGHT && member->GetMapId() == 609 && !member->IsGameMaster() && !member->HasSpell(50977)) + if (member->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TELEPORT) && member->GetMapId() == 609 && !member->IsGameMaster() && !member->HasSpell(50977)) return ERR_GROUP_JOIN_BATTLEGROUND_FAIL; if (!member->GetBGAccessByLevel(bgTemplate->GetBgTypeID())) diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 7bb348caf..dacd0b5c7 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -184,7 +184,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) err = ERR_BATTLEGROUND_QUEUED_FOR_RATED; } // don't let Death Knights join BG queues when they are not allowed to be teleported yet - else if (_player->getClass() == CLASS_DEATH_KNIGHT && _player->GetMapId() == 609 && !_player->IsGameMaster() && !_player->HasSpell(50977)) + else if (_player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_TELEPORT) && _player->GetMapId() == 609 && !_player->IsGameMaster() && !_player->HasSpell(50977)) { err = ERR_BATTLEGROUND_NONE; } diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index e4a1b3c3c..70a2e0513 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -83,7 +83,7 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData) { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); + bool lootAllowed = creature && creature->IsAlive() == (player->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY) && creature->loot.loot_type == LOOT_PICKPOCKETING); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) { player->SendLootError(lguid, lootAllowed ? LOOT_ERROR_TOO_FAR : LOOT_ERROR_DIDNT_KILL); @@ -162,7 +162,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket& /*recvData*/) case HighGuid::Vehicle: { Creature* creature = player->GetMap()->GetCreature(guid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); + bool lootAllowed = creature && creature->IsAlive() == (player->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY) && creature->loot.loot_type == LOOT_PICKPOCKETING); if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE)) { loot = &creature->loot; @@ -382,7 +382,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) { Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid); - bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING); + bool lootAllowed = creature && creature->IsAlive() == (player->IsClass(CLASS_ROGUE, CLASS_CONTEXT_ABILITY) && creature->loot.loot_type == LOOT_PICKPOCKETING); if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) return; diff --git a/src/server/game/Misc/GameGraveyard.cpp b/src/server/game/Misc/GameGraveyard.cpp index 5552a05fd..08d9232bc 100644 --- a/src/server/game/Misc/GameGraveyard.cpp +++ b/src/server/game/Misc/GameGraveyard.cpp @@ -202,7 +202,7 @@ GraveyardStruct const* Graveyard::GetClosestGraveyard(Player* player, TeamId tea GRAVEYARD_ARCHERUS = 1405 }; - if (player->getClass() != CLASS_DEATH_KNIGHT && (graveyardLink.safeLocId == GRAVEYARD_EBON_HOLD || graveyardLink.safeLocId == GRAVEYARD_ARCHERUS)) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_GRAVEYARD) && (graveyardLink.safeLocId == GRAVEYARD_EBON_HOLD || graveyardLink.safeLocId == GRAVEYARD_ARCHERUS)) { continue; } diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index c9574106d..290837d1f 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -1038,6 +1038,19 @@ bool ScriptMgr::CanRepopAtGraveyard(Player* player) return true; } +Optional ScriptMgr::OnPlayerIsClass(Player const* player, Classes unitClass, ClassContext context) +{ + if (ScriptRegistry::ScriptPointerList.empty()) + return {}; + for (auto const& [scriptID, script] : ScriptRegistry::ScriptPointerList) + { + Optional scriptResult = script->OnPlayerIsClass(player, unitClass, context); + if (scriptResult) + return scriptResult; + } + return {}; +} + void ScriptMgr::OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, bool IsPure) { ExecuteScript([&](PlayerScript* script) diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h index 68c67fa00..48c72f726 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h @@ -322,6 +322,8 @@ public: [[nodiscard]] virtual bool CanRepopAtGraveyard(Player* /*player*/) { return true; } + [[nodiscard]] virtual Optional OnPlayerIsClass(Player const* /*player*/, Classes /*playerClass*/, ClassContext /*context*/) { return std::nullopt; } + virtual void OnGetMaxSkillValue(Player* /*player*/, uint32 /*skill*/, int32& /*result*/, bool /*IsPure*/) { } [[nodiscard]] virtual bool OnPlayerHasActivePowerType(Player const* /*player*/, Powers /*power*/) { return false; } diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 0df982ea4..2a7766495 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -401,6 +401,7 @@ public: /* PlayerScript */ bool CanGiveMailRewardAtGiveLevel(Player* player, uint8 level); void OnDeleteFromDB(CharacterDatabaseTransaction trans, uint32 guid); bool CanRepopAtGraveyard(Player* player); + std::optional OnPlayerIsClass(Player const* player, Classes playerClass, ClassContext context); void OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, bool IsPure); bool OnPlayerHasActivePowerType(Player const* player, Powers power); void OnUpdateGatheringSkill(Player* player, uint32 skillId, uint32 currentLevel, uint32 gray, uint32 green, uint32 yellow, uint32& gain); diff --git a/src/server/game/Scripting/ScriptObjectFwd.h b/src/server/game/Scripting/ScriptObjectFwd.h index 67c5e8ca7..a7a899e02 100644 --- a/src/server/game/Scripting/ScriptObjectFwd.h +++ b/src/server/game/Scripting/ScriptObjectFwd.h @@ -79,6 +79,7 @@ class WorldSocket; enum ArenaTeamInfoType : uint8; enum AuraRemoveMode : uint8; enum BattlegroundDesertionType : uint8; +enum ClassContext : uint8; enum ContentLevels : uint8; enum DamageEffectType : uint8; enum EnchantmentSlot : uint8; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 130945722..e333b98ee 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2027,7 +2027,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo if (!target->HasAuraType(SPELL_AURA_MOD_SHAPESHIFT)) { target->SetShapeshiftForm(FORM_NONE); - if (target->getClass() == CLASS_DRUID) + if (target->IsClass(CLASS_DRUID, CLASS_CONTEXT_ABILITY)) { target->setPowerType(POWER_MANA); // Remove movement impairing effects also when shifting out @@ -2106,7 +2106,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo if (target->GetTypeId() == TYPEID_PLAYER) target->ToPlayer()->InitDataForForm(); - if (target->getClass() == CLASS_DRUID) + if (target->IsClass(CLASS_DRUID, CLASS_CONTEXT_ABILITY)) { // Dash if (AuraEffect* aurEff = target->GetAuraEffect(SPELL_AURA_MOD_INCREASE_SPEED, SPELLFAMILY_DRUID, 0, 0, 0x8)) @@ -6038,7 +6038,7 @@ void AuraEffect::HandleAuraConvertRune(AuraApplication const* aurApp, uint8 mode Player* player = target->ToPlayer(); - if (player->getClass() != CLASS_DEATH_KNIGHT) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; uint32 runes = m_amount; @@ -6310,7 +6310,7 @@ void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const { if (target->GetTypeId() != TYPEID_PLAYER) return; - if (target->ToPlayer()->getClass() != CLASS_DEATH_KNIGHT) + if (!target->ToPlayer()->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; // timer expired - remove death runes diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 093823a1f..5e4e67f51 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -1876,7 +1876,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b break; if (target->GetTypeId() != TYPEID_PLAYER) break; - if (target->ToPlayer()->getClass() != CLASS_DEATH_KNIGHT) + if (!target->ToPlayer()->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) break; // aura removed - remove death runes diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 75522d2dd..5939c8fc4 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -4807,7 +4807,7 @@ void Spell::SendSpellGo() } if ((m_caster->GetTypeId() == TYPEID_PLAYER) - && (m_caster->getClass() == CLASS_DEATH_KNIGHT) + && (m_caster->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) && m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNE) { @@ -5396,7 +5396,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 RuneCostID) return SPELL_CAST_OK; } - if (player->getClass() != CLASS_DEATH_KNIGHT) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return SPELL_CAST_OK; SpellRuneCostEntry const* src = sSpellRuneCostStore.LookupEntry(RuneCostID); @@ -5437,7 +5437,7 @@ SpellCastResult Spell::CheckRuneCost(uint32 RuneCostID) void Spell::TakeRunePower(bool didHit) { - if (m_caster->GetTypeId() != TYPEID_PLAYER || m_caster->getClass() != CLASS_DEATH_KNIGHT) + if (m_caster->GetTypeId() != TYPEID_PLAYER || !m_caster->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; SpellRuneCostEntry const* runeCostData = sSpellRuneCostStore.LookupEntry(m_spellInfo->RuneCostID); @@ -6408,7 +6408,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_ALREADY_HAVE_CHARM; } - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK && strict) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->IsClass(CLASS_WARLOCK, CLASS_CONTEXT_PET) && strict) if (Pet* pet = m_caster->ToPlayer()->GetPet()) pet->CastSpell(pet, 32752, true, nullptr, nullptr, pet->GetGUID()); //starting cast, trigger pet stun (cast by pet so it doesn't attack player) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index aaeb85080..97cbbd4d2 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3077,7 +3077,7 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/) if (creatureTarget->IsPet()) return; - if (m_caster->getClass() != CLASS_HUNTER) + if (!m_caster->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) return; // cast finish successfully @@ -3209,7 +3209,7 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id); // Reset cooldowns - if (owner->getClass() != CLASS_HUNTER) + if (!owner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) { pet->m_CreatureSpellCooldowns.clear(); owner->PetSpellInitialize(); @@ -5746,7 +5746,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) Player* player = m_caster->ToPlayer(); - if (player->getClass() != CLASS_DEATH_KNIGHT) + if (!player->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) return; // needed later @@ -5814,7 +5814,7 @@ void Spell::EffectCreateTamedPet(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || unitTarget->getClass() != CLASS_HUNTER) + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || unitTarget->GetPetGUID() || !unitTarget->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) return; uint32 creatureEntry = m_spellInfo->Effects[effIndex].MiscValue; diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp index 3a2ba01ac..81c4e742a 100644 --- a/src/server/scripts/Commands/cs_reset.cpp +++ b/src/server/scripts/Commands/cs_reset.cpp @@ -140,7 +140,7 @@ public: uint8 oldLevel = playerTarget->GetLevel(); // set starting level - uint32 startLevel = playerTarget->getClass() != CLASS_DEATH_KNIGHT + uint32 startLevel = !playerTarget->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_INIT) ? sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index 7f73b5b37..bbc8a0f48 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -1029,7 +1029,7 @@ class spell_class_call_handler : public SpellScript targets.remove_if([spellInfo](WorldObject const* target) -> bool { Player const* player = target->ToPlayer(); - if (!player || player->getClass() == CLASS_DEATH_KNIGHT) // ignore all death knights from whatever spell, for some reason the condition below is not working x.x + if (!player || player->IsClass(CLASS_DEATH_KNIGHT)) // ignore all death knights from whatever spell, for some reason the condition below is not working x.x { return true; } diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp index 8d357de1e..8f6f20e62 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp @@ -447,9 +447,9 @@ public: PlayerAbility_Timer = urand(8000, 10000); PlayerClass = target->getClass() - 1; - if (PlayerClass == CLASS_DRUID - 1) + if (target->IsClass(CLASS_DRUID)) PlayerClass = CLASS_DRUID; - else if (PlayerClass == CLASS_PRIEST - 1 && target->HasSpell(15473)) + else if (target->IsClass(CLASS_PRIEST) && target->HasSpell(15473)) PlayerClass = CLASS_PRIEST; // shadow priest SiphonSoul_Timer = 99999; // buff lasts 30 sec diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index c574da421..df9757da8 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -65,7 +65,7 @@ public: { case GOSSIP_ACTION_INFO_DEF + 1: CloseGossipMenuFor(player); - if (player->getClass() == CLASS_DRUID && player->GetTeamId() == TEAM_HORDE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_TAXI) && player->GetTeamId() == TEAM_HORDE) player->ActivateTaxiPathTo(TAXI_PATH_ID_HORDE); break; case GOSSIP_ACTION_INFO_DEF + 2: @@ -80,29 +80,42 @@ public: bool OnGossipHello(Player* player, Creature* creature) override { - if (player->getClass() != CLASS_DRUID) + if (player->GetTeamId() != TEAM_HORDE) { - SendGossipMenuFor(player, 4916, creature->GetGUID()); - } - else if (player->GetTeamId() != TEAM_HORDE) - { - if (player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_QUEST) && player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, 4042, 2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); } - SendGossipMenuFor(player, 4917, creature->GetGUID()); + if (player->IsClass(CLASS_DRUID)) + { + SendGossipMenuFor(player, 4917, creature->GetGUID()); + } + else + { + SendGossipMenuFor(player, 4916, creature->GetGUID()); + } } - else if (player->getClass() == CLASS_DRUID && player->GetTeamId() == TEAM_HORDE) + else if (player->GetTeamId() == TEAM_HORDE) { - AddGossipItemFor(player, 4042, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_TAXI)) + { + AddGossipItemFor(player, 4042, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } - if (player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_QUEST) && player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, 4042, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); } - SendGossipMenuFor(player, 4918, creature->GetGUID()); + if (player->IsClass(CLASS_DRUID)) + { + SendGossipMenuFor(player, 4918, creature->GetGUID()); + } + else + { + SendGossipMenuFor(player, 4916, creature->GetGUID()); + } } return true; } @@ -176,7 +189,7 @@ public: { case GOSSIP_ACTION_INFO_DEF + 1: CloseGossipMenuFor(player); - if (player->getClass() == CLASS_DRUID && player->GetTeamId() == TEAM_ALLIANCE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_TAXI) && player->GetTeamId() == TEAM_ALLIANCE) player->ActivateTaxiPathTo(TAXI_PATH_ID_ALLY); break; case GOSSIP_ACTION_INFO_DEF + 2: @@ -191,29 +204,41 @@ public: bool OnGossipHello(Player* player, Creature* creature) override { - if (player->getClass() != CLASS_DRUID) + if (player->GetTeamId() != TEAM_ALLIANCE) { - SendGossipMenuFor(player, 4913, creature->GetGUID()); - } - else if (player->GetTeamId() != TEAM_ALLIANCE) - { - if (player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_QUEST) && player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, 4041, 2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); } - - SendGossipMenuFor(player, 4915, creature->GetGUID()); + if (player->IsClass(CLASS_DRUID)) + { + SendGossipMenuFor(player, 4915, creature->GetGUID()); + } + else + { + SendGossipMenuFor(player, 4913, creature->GetGUID()); + } } - else if (player->getClass() == CLASS_DRUID && player->GetTeamId() == TEAM_ALLIANCE) + else if (player->GetTeamId() == TEAM_ALLIANCE) { - AddGossipItemFor(player, 4041, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_TAXI)) + { + AddGossipItemFor(player, 4041, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } - if (player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) + if (player->IsClass(CLASS_DRUID, CLASS_CONTEXT_QUEST) && player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) { AddGossipItemFor(player, 4041, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); } - SendGossipMenuFor(player, 4914, creature->GetGUID()); + if (player->IsClass(CLASS_DRUID)) + { + SendGossipMenuFor(player, 4914, creature->GetGUID()); + } + else + { + SendGossipMenuFor(player, 4913, creature->GetGUID()); + } } return true; } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index b1ab5028e..d21c9935d 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -853,12 +853,12 @@ public: { if (p->getPowerType() != POWER_MANA) return true; - if (p->getClass() == CLASS_HUNTER) + if (p->IsClass(CLASS_HUNTER)) return true; uint8 maxIndex = p->GetMostPointsTalentTree(); - if ((p->getClass() == CLASS_PALADIN && maxIndex >= 1) || (p->getClass() == CLASS_SHAMAN && maxIndex == 1) || (p->getClass() == CLASS_DRUID && maxIndex == 1)) + if ((p->IsClass(CLASS_PALADIN) && maxIndex >= 1) || (p->IsClass(CLASS_SHAMAN) && maxIndex == 1) || (p->IsClass(CLASS_DRUID) && maxIndex == 1)) return true; - if (_removeHealers == ((p->getClass() == CLASS_DRUID && maxIndex == 2) || (p->getClass() == CLASS_PALADIN && maxIndex == 0) || (p->getClass() == CLASS_PRIEST && maxIndex <= 1) || (p->getClass() == CLASS_SHAMAN && maxIndex == 2))) + if (_removeHealers == ((p->IsClass(CLASS_DRUID) && maxIndex == 2) || (p->IsClass(CLASS_PALADIN) && maxIndex == 0) || (p->IsClass(CLASS_PRIEST) && maxIndex <= 1) || (p->IsClass(CLASS_SHAMAN) && maxIndex == 2))) return true; return false; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index cc9516d3a..a8e4c3ce8 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -2856,8 +2856,7 @@ public: { c->AI()->AttackStart(target); DoZoneInCombat(c); - uint8 Class = target->getClass(); - if (Class != CLASS_DRUID) + if (!target->IsClass(CLASS_DRUID)) if (Player* p = target->ToPlayer()) { if (Item* i = p->GetWeaponForAttack(BASE_ATTACK)) @@ -2869,7 +2868,7 @@ public: target->CastSpell(c, 60352, true); // Mirror Image, clone visual appearance } - c->AI()->DoAction(Class); + c->AI()->DoAction(target->getClass()); } } } diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 88a938c40..b8f95822c 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -1014,7 +1014,7 @@ class spell_dk_blood_boil : public SpellScript bool Load() override { _executed = false; - return GetCaster()->GetTypeId() == TYPEID_PLAYER && GetCaster()->getClass() == CLASS_DEATH_KNIGHT; + return GetCaster()->GetTypeId() == TYPEID_PLAYER && GetCaster()->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY); } void HandleAfterHit() @@ -1258,7 +1258,7 @@ class spell_dk_death_gate : public SpellScript SpellCastResult CheckClass() { - if (GetCaster()->getClass() != CLASS_DEATH_KNIGHT) + if (!GetCaster()->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) { SetCustomCastResultMessage(SPELL_CUSTOM_ERROR_MUST_BE_DEATH_KNIGHT); return SPELL_FAILED_CUSTOM_ERROR; diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 6688c5e31..e8cf44042 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -446,7 +446,7 @@ class spell_pet_hit_expertise_scalling : public AuraScript { if (Player* modOwner = GetUnitOwner()->GetSpellModOwner()) { - if (modOwner->getClass() == CLASS_HUNTER) + if (modOwner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS)) amount = CalculatePercent(modOwner->m_modRangedHitChance, 8.0f, 8.0f); else if (modOwner->getPowerType() == POWER_MANA) amount = CalculatePercent(modOwner->m_modSpellHitChance, 17.0f, 8.0f); @@ -459,7 +459,7 @@ class spell_pet_hit_expertise_scalling : public AuraScript { if (Player* modOwner = GetUnitOwner()->GetSpellModOwner()) { - if (modOwner->getClass() == CLASS_HUNTER) + if (modOwner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS)) amount = CalculatePercent(modOwner->m_modRangedHitChance, 8.0f, 17.0f); else if (modOwner->getPowerType() == POWER_MANA) amount = CalculatePercent(modOwner->m_modSpellHitChance, 17.0f, 17.0f); @@ -472,7 +472,7 @@ class spell_pet_hit_expertise_scalling : public AuraScript { if (Player* modOwner = GetUnitOwner()->GetSpellModOwner()) { - if (modOwner->getClass() == CLASS_HUNTER) + if (modOwner->IsClass(CLASS_HUNTER, CLASS_CONTEXT_STATS)) amount = CalculatePercent(modOwner->m_modRangedHitChance, 8.0f, 26.0f); else if (modOwner->getPowerType() == POWER_MANA) amount = CalculatePercent(modOwner->m_modSpellHitChance, 17.0f, 26.0f); @@ -1673,7 +1673,7 @@ class spell_gen_pet_summoned : public SpellScript Player* player = GetCaster()->ToPlayer(); if (player->GetLastPetNumber() && player->CanResummonPet(player->GetLastPetSpell())) { - PetType newPetType = (player->getClass() == CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET; + PetType newPetType = (player->IsClass(CLASS_HUNTER, CLASS_CONTEXT_PET)) ? HUNTER_PET : SUMMON_PET; Pet* newPet = new Pet(player, newPetType); if (newPet->LoadPetFromDB(player, 0, player->GetLastPetNumber(), true, 100)) { @@ -3545,11 +3545,11 @@ class spell_gen_on_tournament_mount : public AuraScript case NPC_ARGENT_WARHORSE: { if (player->HasAchieved(ACHIEVEMENT_CHAMPION_ALLIANCE) || player->HasAchieved(ACHIEVEMENT_CHAMPION_HORDE)) - return player->getClass() == CLASS_DEATH_KNIGHT ? SPELL_PENNANT_EBON_BLADE_CHAMPION : SPELL_PENNANT_ARGENT_CRUSADE_CHAMPION; + return player->IsClass(CLASS_DEATH_KNIGHT) ? SPELL_PENNANT_EBON_BLADE_CHAMPION : SPELL_PENNANT_ARGENT_CRUSADE_CHAMPION; else if (player->HasAchieved(ACHIEVEMENT_ARGENT_VALOR)) - return player->getClass() == CLASS_DEATH_KNIGHT ? SPELL_PENNANT_EBON_BLADE_VALIANT : SPELL_PENNANT_ARGENT_CRUSADE_VALIANT; + return player->IsClass(CLASS_DEATH_KNIGHT) ? SPELL_PENNANT_EBON_BLADE_VALIANT : SPELL_PENNANT_ARGENT_CRUSADE_VALIANT; else - return player->getClass() == CLASS_DEATH_KNIGHT ? SPELL_PENNANT_EBON_BLADE_ASPIRANT : SPELL_PENNANT_ARGENT_CRUSADE_ASPIRANT; + return player->IsClass(CLASS_DEATH_KNIGHT) ? SPELL_PENNANT_EBON_BLADE_ASPIRANT : SPELL_PENNANT_ARGENT_CRUSADE_ASPIRANT; } default: return 0; diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index da7c3e997..d0c6880b5 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -3469,7 +3469,7 @@ class spell_item_refocus : public SpellScript { Player* caster = GetCaster()->ToPlayer(); - if (!caster || caster->getClass() != CLASS_HUNTER) + if (!caster || !caster->IsClass(CLASS_HUNTER, CLASS_CONTEXT_ABILITY)) return; caster->RemoveCategoryCooldown(SPELL_CATEGORY_AIMED_MULTI); From 04266cc18d2f32c35cf8e5e47401c5e0754a4beb Mon Sep 17 00:00:00 2001 From: stoudtlr <36058236+stoudtlr@users.noreply.github.com> Date: Sat, 10 Feb 2024 12:49:40 -0500 Subject: [PATCH 14/17] fix(Deps): Fixed finding openssl 3.2 on windows (#18284) * Cherry-pick commit (https://github.com/TrinityCore/TrinityCore/commit/94aed6896873179b284b8b1911590ed9587466d6) Co-authored-by: github.com/Shauren --- src/cmake/macros/FindOpenSSL.cmake | 357 ++++++++++++++++++++--------- 1 file changed, 254 insertions(+), 103 deletions(-) diff --git a/src/cmake/macros/FindOpenSSL.cmake b/src/cmake/macros/FindOpenSSL.cmake index fb1651e6d..6eeafdf9b 100644 --- a/src/cmake/macros/FindOpenSSL.cmake +++ b/src/cmake/macros/FindOpenSSL.cmake @@ -7,15 +7,29 @@ FindOpenSSL Find the OpenSSL encryption library. +This module finds an installed OpenSSL library and determines its version. + +.. versionadded:: 3.19 + When a version is requested, it can be specified as a simple value or as a + range. For a detailed description of version range usage and capabilities, + refer to the :command:`find_package` command. + +.. versionadded:: 3.18 + Support for OpenSSL 3.0. + Optional COMPONENTS ^^^^^^^^^^^^^^^^^^^ +.. versionadded:: 3.12 + This module supports two optional COMPONENTS: ``Crypto`` and ``SSL``. Both components have associated imported targets, as described below. Imported Targets ^^^^^^^^^^^^^^^^ +.. versionadded:: 3.4 + This module defines the following :prop_tgt:`IMPORTED` targets: ``OpenSSL::SSL`` @@ -23,6 +37,8 @@ This module defines the following :prop_tgt:`IMPORTED` targets: ``OpenSSL::Crypto`` The OpenSSL ``crypto`` library, if found. ``OpenSSL::applink`` + .. versionadded:: 3.18 + The OpenSSL ``applink`` components that might be need to be compiled into projects under MSVC. This target is available only if found OpenSSL version is not less than 0.9.8. By linking this target the above OpenSSL targets can @@ -74,36 +90,115 @@ This module will set the following variables in your project: Hints ^^^^^ -Set ``OPENSSL_ROOT_DIR`` to the root directory of an OpenSSL installation. -Set ``OPENSSL_USE_STATIC_LIBS`` to ``TRUE`` to look for static libraries. -Set ``OPENSSL_MSVC_STATIC_RT`` set ``TRUE`` to choose the MT version of the lib. +The following variables may be set to control search behavior: + +``OPENSSL_ROOT_DIR`` + Set to the root directory of an OpenSSL installation. + +``OPENSSL_USE_STATIC_LIBS`` + .. versionadded:: 3.4 + + Set to ``TRUE`` to look for static libraries. + +``OPENSSL_MSVC_STATIC_RT`` + .. versionadded:: 3.5 + + Set to ``TRUE`` to choose the MT version of the lib. + +``ENV{PKG_CONFIG_PATH}`` + On UNIX-like systems, ``pkg-config`` is used to locate the system OpenSSL. + Set the ``PKG_CONFIG_PATH`` environment variable to look in alternate + locations. Useful on multi-lib systems. #]=======================================================================] -set(OPENSSL_EXPECTED_VERSION "1.0") - macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library) - if((CMAKE_SYSTEM_NAME STREQUAL "Linux") AND + unset(_OpenSSL_extra_static_deps) + if(UNIX AND (("${ssl_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$") OR ("${crypto_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$"))) set(_OpenSSL_has_dependencies TRUE) - find_package(Threads) + unset(_OpenSSL_has_dependency_zlib) + if(OPENSSL_USE_STATIC_LIBS) + set(_OpenSSL_libs "${_OPENSSL_STATIC_LIBRARIES}") + set(_OpenSSL_ldflags_other "${_OPENSSL_STATIC_LDFLAGS_OTHER}") + else() + set(_OpenSSL_libs "${_OPENSSL_LIBRARIES}") + set(_OpenSSL_ldflags_other "${_OPENSSL_LDFLAGS_OTHER}") + endif() + if(_OpenSSL_libs) + unset(_OpenSSL_has_dependency_dl) + foreach(_OPENSSL_DEP_LIB IN LISTS _OpenSSL_libs) + if (_OPENSSL_DEP_LIB STREQUAL "ssl" OR _OPENSSL_DEP_LIB STREQUAL "crypto") + # ignoring: these are the targets + elseif(_OPENSSL_DEP_LIB STREQUAL CMAKE_DL_LIBS) + set(_OpenSSL_has_dependency_dl TRUE) + elseif(_OPENSSL_DEP_LIB STREQUAL "z") + find_package(ZLIB) + set(_OpenSSL_has_dependency_zlib TRUE) + else() + list(APPEND _OpenSSL_extra_static_deps "${_OPENSSL_DEP_LIB}") + endif() + endforeach() + unset(_OPENSSL_DEP_LIB) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(_OpenSSL_has_dependency_dl TRUE) + endif() + if(_OpenSSL_ldflags_other) + unset(_OpenSSL_has_dependency_threads) + foreach(_OPENSSL_DEP_LDFLAG IN LISTS _OpenSSL_ldflags_other) + if (_OPENSSL_DEP_LDFLAG STREQUAL "-pthread") + set(_OpenSSL_has_dependency_threads TRUE) + find_package(Threads) + endif() + endforeach() + unset(_OPENSSL_DEP_LDFLAG) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(_OpenSSL_has_dependency_threads TRUE) + find_package(Threads) + endif() + unset(_OpenSSL_libs) + unset(_OpenSSL_ldflags_other) else() set(_OpenSSL_has_dependencies FALSE) endif() endmacro() function(_OpenSSL_add_dependencies libraries_var) - if(CMAKE_THREAD_LIBS_INIT) + if(_OpenSSL_has_dependency_zlib) + list(APPEND ${libraries_var} ${ZLIB_LIBRARY}) + endif() + if(_OpenSSL_has_dependency_threads) list(APPEND ${libraries_var} ${CMAKE_THREAD_LIBS_INIT}) endif() - list(APPEND ${libraries_var} ${CMAKE_DL_LIBS}) + if(_OpenSSL_has_dependency_dl) + list(APPEND ${libraries_var} ${CMAKE_DL_LIBS}) + endif() + list(APPEND ${libraries_var} ${_OpenSSL_extra_static_deps}) set(${libraries_var} ${${libraries_var}} PARENT_SCOPE) endfunction() function(_OpenSSL_target_add_dependencies target) if(_OpenSSL_has_dependencies) - set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads ) - set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} ) + if(_OpenSSL_has_dependency_zlib) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ZLIB::ZLIB ) + endif() + if(_OpenSSL_has_dependency_threads) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() + if(_OpenSSL_has_dependency_dl) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} ) + endif() + if(_OpenSSL_extra_static_deps) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_OpenSSL_extra_static_deps}) + endif() + endif() + if(WIN32 AND OPENSSL_USE_STATIC_LIBS) + if(WINCE) + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ws2 ) + else() + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ws2_32 ) + endif() + set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES crypt32 ) endif() endfunction() @@ -115,62 +210,87 @@ endif () # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES if(OPENSSL_USE_STATIC_LIBS) set(_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) + if(MSVC) set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) else() set(CMAKE_FIND_LIBRARY_SUFFIXES .a ) endif() endif() -if (WIN32) +if(CMAKE_SYSTEM_NAME STREQUAL "QNX" AND + CMAKE_SYSTEM_VERSION VERSION_GREATER_EQUAL "7.0" AND CMAKE_SYSTEM_VERSION VERSION_LESS "7.1" AND + OpenSSL_FIND_VERSION VERSION_GREATER_EQUAL "1.1" AND OpenSSL_FIND_VERSION VERSION_LESS "1.2") + # QNX 7.0.x provides openssl 1.0.2 and 1.1.1 in parallel: + # * openssl 1.0.2: libcrypto.so.2 and libssl.so.2, headers under usr/include/openssl + # * openssl 1.1.1: libcrypto1_1.so.2.1 and libssl1_1.so.2.1, header under usr/include/openssl1_1 + # See http://www.qnx.com/developers/articles/rel_6726_0.html + set(_OPENSSL_FIND_PATH_SUFFIX "openssl1_1") + set(_OPENSSL_NAME_POSTFIX "1_1") +else() + set(_OPENSSL_FIND_PATH_SUFFIX "include") +endif() + +if (OPENSSL_ROOT_DIR OR NOT "$ENV{OPENSSL_ROOT_DIR}" STREQUAL "") + set(_OPENSSL_ROOT_HINTS HINTS ${OPENSSL_ROOT_DIR} ENV OPENSSL_ROOT_DIR) + set(_OPENSSL_ROOT_PATHS NO_DEFAULT_PATH) +elseif (MSVC) # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_MSI_INSTALL_GUID "") - if(PLATFORM EQUAL 64) - set(_OPENSSL_MSI_INSTALL_GUID "117551DB-A110-4BBD-BB05-CFE0BCB3ED31") - set(_OPENSSL_ROOT_HINTS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR - ) + set(_OPENSSL_MSI_INSTALL_GUIDS "") + + if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") + set(_arch "Win64-ARM") + set(_OPENSSL_MSI_INSTALL_GUIDS "99C28AFA-6419-40B1-B88D-32B810BB4234") + else() + set(_arch "Win64") + set(_OPENSSL_MSI_INSTALL_GUIDS "117551DB-A110-4BBD-BB05-CFE0BCB3ED31" "50A9FBE2-0F8C-4D5D-97A4-A63A71C4EA1E") + endif() file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "${_programfiles}/OpenSSL" - "${_programfiles}/OpenSSL-Win64" - "C:/OpenSSL/" - "C:/OpenSSL-Win64/" - ) + set(_OPENSSL_ROOT_HINTS HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]") else() - set(_OPENSSL_MSI_INSTALL_GUID "A1EEC576-43B9-4E75-9E02-03DA542D2A38") - set(_OPENSSL_ROOT_HINTS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR - ) - file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "${_programfiles}/OpenSSL" - "${_programfiles}/OpenSSL-Win32" - "C:/OpenSSL/" - "C:/OpenSSL-Win32/" - ) + set(_arch "Win32") + set(_progfiles_x86 "ProgramFiles(x86)") + if(NOT "$ENV{${_progfiles_x86}}" STREQUAL "") + # under windows 64 bit machine + file(TO_CMAKE_PATH "$ENV{${_progfiles_x86}}" _programfiles) + else() + # under windows 32 bit machine + file(TO_CMAKE_PATH "$ENV{ProgramFiles}" _programfiles) + endif() + set(_OPENSSL_ROOT_HINTS HINTS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]") + set(_OPENSSL_MSI_INSTALL_GUIDS "A1EEC576-43B9-4E75-9E02-03DA542D2A38" "31D2408A-9CAE-4988-9EC3-F40FDE7D6AE5") endif() - unset(_programfiles) + # If OpenSSL was installed using .msi package instead of .exe, Inno Setup registry values are not written to Uninstall\OpenSSL # but because it is only a shim around Inno Setup it does write the location of uninstaller which we can use to determine path - get_filename_component(_OPENSSL_MSI_INSTALL_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Inno Setup MSIs\\${_OPENSSL_MSI_INSTALL_GUID};]" DIRECTORY) - if(NOT _OPENSSL_MSI_INSTALL_PATH STREQUAL "/") - list(INSERT _OPENSSL_ROOT_HINTS 0 ${_OPENSSL_MSI_INSTALL_PATH}) - endif() -else () - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR + foreach(_OPENSSL_MSI_INSTALL_GUID IN LISTS _OPENSSL_MSI_INSTALL_GUIDS) + get_filename_component(_OPENSSL_MSI_INSTALL_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Inno Setup MSIs\\${_OPENSSL_MSI_INSTALL_GUID};]" DIRECTORY) + if(NOT _OPENSSL_MSI_INSTALL_PATH STREQUAL "/") + list(INSERT _OPENSSL_ROOT_HINTS 2 ${_OPENSSL_MSI_INSTALL_PATH}) + endif() + endforeach() + unset(_OPENSSL_MSI_INSTALL_GUIDS) + + set(_OPENSSL_ROOT_PATHS + PATHS + "${_programfiles}/OpenSSL" + "${_programfiles}/OpenSSL-${_arch}" + "C:/OpenSSL/" + "C:/OpenSSL-${_arch}/" ) + unset(_programfiles) + unset(_arch) endif () +if(HOMEBREW_PREFIX) + list(APPEND _OPENSSL_ROOT_HINTS + "${HOMEBREW_PREFIX}/opt/openssl@1.1" + "${HOMEBREW_PREFIX}/opt/openssl@3") +endif() + set(_OPENSSL_ROOT_HINTS_AND_PATHS - HINTS ${_OPENSSL_ROOT_HINTS} - PATHS ${_OPENSSL_ROOT_PATHS} + ${_OPENSSL_ROOT_HINTS} + ${_OPENSSL_ROOT_PATHS} ) find_path(OPENSSL_INCLUDE_DIR @@ -181,7 +301,7 @@ find_path(OPENSSL_INCLUDE_DIR ${_OPENSSL_INCLUDEDIR} ${_OPENSSL_INCLUDE_DIRS} PATH_SUFFIXES - include + ${_OPENSSL_FIND_PATH_SUFFIX} ) if(WIN32 AND NOT CYGWIN) @@ -210,9 +330,15 @@ if(WIN32 AND NOT CYGWIN) # Since OpenSSL 1.1, lib names are like libcrypto32MTd.lib and libssl32MTd.lib if( "${CMAKE_SIZEOF_VOID_P}" STREQUAL "8" ) - set(_OPENSSL_MSVC_ARCH_SUFFIX "64") + set(_OPENSSL_MSVC_ARCH_SUFFIX "64") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") + set(_OPENSSL_MSVC_ARCH_DIRECTORY "arm64") + else() + set(_OPENSSL_MSVC_ARCH_DIRECTORY "x64") + endif() else() - set(_OPENSSL_MSVC_ARCH_SUFFIX "32") + set(_OPENSSL_MSVC_ARCH_SUFFIX "32") + set(_OPENSSL_MSVC_ARCH_DIRECTORY "x86") endif() if(OPENSSL_USE_STATIC_LIBS) @@ -237,29 +363,40 @@ if(WIN32 AND NOT CYGWIN) find_library(LIB_EAY_DEBUG NAMES - # When OpenSSL is built with default options, the static library name is suffixed with "_static". - # Looking the "libcrypto_static.lib" with a higher priority than "libcrypto.lib" which is the - # import library of "libcrypto.dll". - libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libcrypto${_OPENSSL_STATIC_SUFFIX}d - libeay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libeay32${_OPENSSL_STATIC_SUFFIX}d - crypto${_OPENSSL_STATIC_SUFFIX}d - # When OpenSSL is built with the "-static" option, only the static build is produced, - # and it is not suffixed with "_static". - libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libcrypto${_OPENSSL_MSVC_RT_MODE}d - libcryptod - libeay32${_OPENSSL_MSVC_RT_MODE}d - libeay32d - cryptod + libcrypto${_OPENSSL_STATIC_SUFFIX} NAMES_PER_DIR ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES - ${_OPENSSL_PATH_SUFFIXES} + "lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}d" ) + if(NOT LIB_EAY_DEBUG) + find_library(LIB_EAY_DEBUG + NAMES + # When OpenSSL is built with default options, the static library name is suffixed with "_static". + # Looking the "libcrypto_static.lib" with a higher priority than "libcrypto.lib" which is the + # import library of "libcrypto.dll". + libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libcrypto${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libcrypto${_OPENSSL_STATIC_SUFFIX}d + libeay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libeay32${_OPENSSL_STATIC_SUFFIX}d + crypto${_OPENSSL_STATIC_SUFFIX}d + # When OpenSSL is built with the "-static" option, only the static build is produced, + # and it is not suffixed with "_static". + libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libcrypto${_OPENSSL_MSVC_RT_MODE}d + libcryptod + libeay32${_OPENSSL_MSVC_RT_MODE}d + libeay32d + cryptod + NAMES_PER_DIR + ${_OPENSSL_ROOT_HINTS_AND_PATHS} + PATH_SUFFIXES + ${_OPENSSL_PATH_SUFFIXES} + ) + endif() + find_library(LIB_EAY_RELEASE NAMES # When OpenSSL is built with default options, the static library name is suffixed with "_static". @@ -283,33 +420,45 @@ if(WIN32 AND NOT CYGWIN) ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES ${_OPENSSL_PATH_SUFFIXES} + "lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}" ) find_library(SSL_EAY_DEBUG NAMES - # When OpenSSL is built with default options, the static library name is suffixed with "_static". - # Looking the "libssl_static.lib" with a higher priority than "libssl.lib" which is the - # import library of "libssl.dll". - libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libssl${_OPENSSL_STATIC_SUFFIX}d - ssleay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - ssleay32${_OPENSSL_STATIC_SUFFIX}d - ssl${_OPENSSL_STATIC_SUFFIX}d - # When OpenSSL is built with the "-static" option, only the static build is produced, - # and it is not suffixed with "_static". - libssl${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libssl${_OPENSSL_MSVC_RT_MODE}d - libssld - ssleay32${_OPENSSL_MSVC_RT_MODE}d - ssleay32d - ssld + libssl${_OPENSSL_STATIC_SUFFIX} NAMES_PER_DIR ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES - ${_OPENSSL_PATH_SUFFIXES} + "lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}d" ) + if(NOT SSL_EAY_DEBUG) + find_library(SSL_EAY_DEBUG + NAMES + # When OpenSSL is built with default options, the static library name is suffixed with "_static". + # Looking the "libssl_static.lib" with a higher priority than "libssl.lib" which is the + # import library of "libssl.dll". + libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libssl${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libssl${_OPENSSL_STATIC_SUFFIX}d + ssleay32${_OPENSSL_STATIC_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + ssleay32${_OPENSSL_STATIC_SUFFIX}d + ssl${_OPENSSL_STATIC_SUFFIX}d + # When OpenSSL is built with the "-static" option, only the static build is produced, + # and it is not suffixed with "_static". + libssl${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d + libssl${_OPENSSL_MSVC_RT_MODE}d + libssld + ssleay32${_OPENSSL_MSVC_RT_MODE}d + ssleay32d + ssld + NAMES_PER_DIR + ${_OPENSSL_ROOT_HINTS_AND_PATHS} + PATH_SUFFIXES + ${_OPENSSL_PATH_SUFFIXES} + ) + endif() + find_library(SSL_EAY_RELEASE NAMES # When OpenSSL is built with default options, the static library name is suffixed with "_static". @@ -333,6 +482,7 @@ if(WIN32 AND NOT CYGWIN) ${_OPENSSL_ROOT_HINTS_AND_PATHS} PATH_SUFFIXES ${_OPENSSL_PATH_SUFFIXES} + "lib/VC/${_OPENSSL_MSVC_ARCH_DIRECTORY}/${_OPENSSL_MSVC_RT_MODE}" ) set(LIB_EAY_LIBRARY_DEBUG "${LIB_EAY_DEBUG}") @@ -360,6 +510,7 @@ if(WIN32 AND NOT CYGWIN) PATH_SUFFIXES "lib/MinGW" "lib" + "lib64" ) find_library(SSL_EAY @@ -370,6 +521,7 @@ if(WIN32 AND NOT CYGWIN) PATH_SUFFIXES "lib/MinGW" "lib" + "lib64" ) mark_as_advanced(SSL_EAY LIB_EAY) @@ -411,7 +563,7 @@ else() find_library(OPENSSL_SSL_LIBRARY NAMES - ssl + ssl${_OPENSSL_NAME_POSTFIX} ssleay32 ssleay32MD NAMES_PER_DIR @@ -420,19 +572,19 @@ else() ${_OPENSSL_LIBDIR} ${_OPENSSL_LIBRARY_DIRS} PATH_SUFFIXES - lib + lib lib64 ) find_library(OPENSSL_CRYPTO_LIBRARY NAMES - crypto + crypto${_OPENSSL_NAME_POSTFIX} NAMES_PER_DIR ${_OPENSSL_ROOT_HINTS_AND_PATHS} HINTS ${_OPENSSL_LIBDIR} ${_OPENSSL_LIBRARY_DIRS} PATH_SUFFIXES - lib + lib lib64 ) mark_as_advanced(OPENSSL_CRYPTO_LIBRARY OPENSSL_SSL_LIBRARY) @@ -570,14 +722,6 @@ find_package_handle_standard_args(OpenSSL mark_as_advanced(OPENSSL_INCLUDE_DIR) if(OPENSSL_FOUND) - message(STATUS "Found OpenSSL library: ${OPENSSL_LIBRARIES}") - message(STATUS "Found OpenSSL headers: ${OPENSSL_INCLUDE_DIR}") - include(EnsureVersion) - ENSURE_VERSION("${OPENSSL_EXPECTED_VERSION}" "${OPENSSL_VERSION}" OPENSSL_VERSION_OK) - if(NOT OPENSSL_VERSION_OK) - message(FATAL_ERROR "AzerothCore needs OpenSSL version ${OPENSSL_EXPECTED_VERSION} but found too new version ${OPENSSL_VERSION}. AzerothCore needs OpenSSL 1.0.x or 1.1.x to work properly. If you still have problems please install OpenSSL 1.0.x if you still have problems search on forum for TCE00022") - endif() - if(NOT TARGET OpenSSL::Crypto AND (EXISTS "${OPENSSL_CRYPTO_LIBRARY}" OR EXISTS "${LIB_EAY_LIBRARY_DEBUG}" OR @@ -642,7 +786,7 @@ if(OPENSSL_FOUND) _OpenSSL_target_add_dependencies(OpenSSL::SSL) endif() - if("${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_FIX}" VERSION_GREATER_EQUAL "0.9.8") + if("${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}" VERSION_GREATER_EQUAL "0.9.8") if(MSVC) if(EXISTS "${OPENSSL_INCLUDE_DIR}") set(_OPENSSL_applink_paths PATHS ${OPENSSL_INCLUDE_DIR}) @@ -669,3 +813,10 @@ endif() if(OPENSSL_USE_STATIC_LIBS) set(CMAKE_FIND_LIBRARY_SUFFIXES ${_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) endif() + +unset(_OPENSSL_FIND_PATH_SUFFIX) +unset(_OPENSSL_NAME_POSTFIX) +unset(_OpenSSL_extra_static_deps) +unset(_OpenSSL_has_dependency_dl) +unset(_OpenSSL_has_dependency_threads) +unset(_OpenSSL_has_dependency_zlib) From 6b1dd7e19ef52c2760d65849a31a29af70ad9795 Mon Sep 17 00:00:00 2001 From: Walter Pagani Date: Sat, 10 Feb 2024 17:31:17 -0300 Subject: [PATCH 15/17] fix(Core/Database) mysql_stmt_bind_param deprecated in MySQL 8.3 (#18295) * feat(fix/build) Test on MacOS * Syntax error * Syntax error * Adding missing parameters * typo * deprecated * Check MySQL version --- src/server/database/Database/MySQLConnection.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index 3e776059a..d3c8fc6d7 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -227,7 +227,11 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt) uint32 _s = getMSTime(); +#if MYSQL_VERSION_ID >= 80300 + if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr)) +#else if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) +#endif { uint32 lErrno = mysql_errno(m_Mysql); LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT)); @@ -275,7 +279,11 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement uint32 _s = getMSTime(); +#if MYSQL_VERSION_ID >= 80300 + if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr)) +#else if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) +#endif { uint32 lErrno = mysql_errno(m_Mysql); LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT)); From 1d8c4056b3b8671fef2e356d940cef493de5f6d5 Mon Sep 17 00:00:00 2001 From: AnchyDev <35346484+AnchyDev@users.noreply.github.com> Date: Sun, 11 Feb 2024 07:58:49 +1100 Subject: [PATCH 16/17] feat(Core/Hooks): Add the KillRewarder reference to the OnRewardKillRewarder hook. (#18290) * Another hackfix attempt at warden payload forcechecks ban fix. * Revert last commit, interrupt all forcechecks instead of just _dataSent ones. * Add rewarder parameter to OnRewardKillRewarder hook. * Undo old warden changes. * Removed too much! --- src/server/game/Entities/Player/KillRewarder.cpp | 13 ++++++++++++- src/server/game/Entities/Player/KillRewarder.h | 2 ++ .../game/Scripting/ScriptDefines/PlayerScript.cpp | 4 ++-- .../game/Scripting/ScriptDefines/PlayerScript.h | 3 ++- src/server/game/Scripting/ScriptMgr.h | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp index 50d83a692..04ba9ca1d 100644 --- a/src/server/game/Entities/Player/KillRewarder.cpp +++ b/src/server/game/Entities/Player/KillRewarder.cpp @@ -204,12 +204,13 @@ void KillRewarder::_RewardPlayer(Player* player, bool isDungeon) if (_victim->GetTypeId() == TYPEID_PLAYER) player->KilledPlayerCredit(); } + // Give XP only in PvE or in battlegrounds. // Give reputation and kill credit only in PvE. if (!_isPvP || _isBattleGround) { float xpRate = _group ? _groupRate * float(player->GetLevel()) / _aliveSumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels. - sScriptMgr->OnRewardKillRewarder(player, isDungeon, xpRate); // Personal rate is 100%. + sScriptMgr->OnRewardKillRewarder(player, this, isDungeon, xpRate); // Personal rate is 100%. if (_xp) { @@ -290,3 +291,13 @@ void KillRewarder::Reward() if (Map* map = _victim->FindMap()) map->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim); } + +Unit* KillRewarder::GetVictim() +{ + return _victim; +} + +Player* KillRewarder::GetKiller() +{ + return _killer; +} diff --git a/src/server/game/Entities/Player/KillRewarder.h b/src/server/game/Entities/Player/KillRewarder.h index 6f1fda257..3efc3cc62 100644 --- a/src/server/game/Entities/Player/KillRewarder.h +++ b/src/server/game/Entities/Player/KillRewarder.h @@ -30,6 +30,8 @@ public: KillRewarder(Player* killer, Unit* victim, bool isBattleGround); void Reward(); + Unit* GetVictim(); + Player* GetKiller(); private: void _InitXP(Player* player); diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index 290837d1f..f5b777245 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -992,11 +992,11 @@ void ScriptMgr::PetitionShowList(Player* player, Creature* creature, uint32& Cha }); } -void ScriptMgr::OnRewardKillRewarder(Player* player, bool isDungeon, float& rate) +void ScriptMgr::OnRewardKillRewarder(Player* player, KillRewarder* rewarder, bool isDungeon, float& rate) { ExecuteScript([&](PlayerScript* script) { - script->OnRewardKillRewarder(player, isDungeon, rate); + script->OnRewardKillRewarder(player, rewarder, isDungeon, rate); }); } diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h index 48c72f726..a0f9aa57a 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h @@ -22,6 +22,7 @@ // TODO to remove #include "AchievementMgr.h" +#include "KillRewarder.h" class PlayerScript : public ScriptObject { @@ -314,7 +315,7 @@ public: virtual void PetitionShowList(Player* /*player*/, Creature* /*creature*/, uint32& /*CharterEntry*/, uint32& /*CharterDispayID*/, uint32& /*CharterCost*/) { } - virtual void OnRewardKillRewarder(Player* /*player*/, bool /*isDungeon*/, float& /*rate*/) { } + virtual void OnRewardKillRewarder(Player* /*player*/, KillRewarder* /*rewarder*/, bool /*isDungeon*/, float& /*rate*/) { } [[nodiscard]] virtual bool CanGiveMailRewardAtGiveLevel(Player* /*player*/, uint8 /*level*/) { return true; } diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 2a7766495..a242119bc 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -397,7 +397,7 @@ public: /* PlayerScript */ bool CanSendMail(Player* player, ObjectGuid receiverGuid, ObjectGuid mailbox, std::string& subject, std::string& body, uint32 money, uint32 COD, Item* item); void PetitionBuy(Player* player, Creature* creature, uint32& charterid, uint32& cost, uint32& type); void PetitionShowList(Player* player, Creature* creature, uint32& CharterEntry, uint32& CharterDispayID, uint32& CharterCost); - void OnRewardKillRewarder(Player* player, bool isDungeon, float& rate); + void OnRewardKillRewarder(Player* player, KillRewarder* rewarder, bool isDungeon, float& rate); bool CanGiveMailRewardAtGiveLevel(Player* player, uint8 level); void OnDeleteFromDB(CharacterDatabaseTransaction trans, uint32 guid); bool CanRepopAtGraveyard(Player* player); From f18145ca12890ca1c2d5f6a3595943c81b2608e1 Mon Sep 17 00:00:00 2001 From: Walter Pagani Date: Sun, 11 Feb 2024 10:28:43 -0300 Subject: [PATCH 17/17] feat(fix/build) ignore mariadb in the conditional (#18310) * feat(fix/build) ignore mariadb in the conditional * Parentheses in the second condition --- src/server/database/Database/MySQLConnection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index d3c8fc6d7..a77f78d07 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -227,7 +227,7 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt) uint32 _s = getMSTime(); -#if MYSQL_VERSION_ID >= 80300 +#if !defined(MARIADB_VERSION_ID) && (MYSQL_VERSION_ID >= 80300) if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr)) #else if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) @@ -279,7 +279,7 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement uint32 _s = getMSTime(); -#if MYSQL_VERSION_ID >= 80300 +#if !defined(MARIADB_VERSION_ID) && (MYSQL_VERSION_ID >= 80300) if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr)) #else if (mysql_stmt_bind_param(msql_STMT, msql_BIND))