From 7e0dff58392f11ea969a41f4e780292dd63f80d0 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Thu, 27 Jun 2024 18:03:19 +0200 Subject: [PATCH] fix(Scripts/Karazhan) Nightbane take off phase handling (#18934) * nightbane fly skip ground phase use conditional schedule takeoff instead invincibility fix and timing no need to delay ScheduleGround * (WIP) cursed boss new inro lands attack flag is not removed.. cant attack :) * fix intro, refactor fly to air, handle waypoints, air facing, urn,... * add PassiveAI header * revert removal of helper target on roof --- .../rev_1716758365231980439.sql | 29 ++ .../Karazhan/boss_nightbane.cpp | 414 +++++++++++------- 2 files changed, 296 insertions(+), 147 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1716758365231980439.sql diff --git a/data/sql/updates/pending_db_world/rev_1716758365231980439.sql b/data/sql/updates/pending_db_world/rev_1716758365231980439.sql new file mode 100644 index 000000000..ddf0f6465 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1716758365231980439.sql @@ -0,0 +1,29 @@ +-- +-- FORCE_GOSSIP +UPDATE `creature_template` SET `detection_range` = 40, `type_flags` = `type_flags` | 134217728, `movementId` = 1988 WHERE `entry` = 17225; + +-- Intro +DELETE FROM `waypoint_data` WHERE `id` = 172250; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(172250, 1, -11105.76, -1875.8998, 158.97804, NULL, 0, 2, 0, 100, 0), +(172250, 2, -11175.097, -1857.2238, 101.00588, NULL, 0, 2, 0, 100, 0), +(172250, 3, -11296.927, -1764.5311, 101.00588, NULL, 0, 2, 0, 100, 0), +(172250, 4, -11258.901, -1722.3717, 101.00588, NULL, 0, 2, 0, 100, 0), +(172250, 5, -11176.764, -1809.5985, 101.00588, NULL, 0, 2, 0, 100, 0), +(172250, 6, -11191.107, -1889.3965, 107.89479, NULL, 0, 2, 0, 100, 0), +(172250, 7, -11152.18, -1863.318, 101.00588, NULL, 0, 2, 0, 100, 0), +(172250, 8, -11130.677, -1891.4235, 107.89634, NULL, 0, 2, 0, 100, 0), +(172250, 9, -11110.674, -1878.7712, 107.89686, NULL, 0, 2, 0, 100, 0); + +-- Landing +DELETE FROM `waypoint_data` WHERE `id` = 172251; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(172251, 1, -11117.731, -1941.2609, 127.17197, NUll, 0, 2, 0, 100, 0), +(172251, 2, -11123.777, -1968.243, 125.92196, NUll, 0, 2, 0, 100, 0), +(172251, 3, -11148.344, -1972.9801, 116.69972, NUll, 0, 2, 0, 100, 0), +(172251, 4, -11161.62, -1945.7255, 103.08859, NUll, 0, 2, 0, 100, 0), +(172251, 5, -11168.009, -1922.9045, 97.39415, NUll, 0, 2, 0, 100, 0), +(172251, 6, -11162.231, -1900.3287, 94.72747, NUll, 0, 2, 0, 100, 0); + +-- Nightbane helper target +UPDATE `creature_template` SET `type_flags` = `type_flags` | 134217728, `movementId` = 1693, `ScriptName` = 'npc_nightbane_helper_target' WHERE `entry` = 17260; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp index ec7bd7bfe..c0dbd8fdc 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp @@ -20,22 +20,23 @@ #include "Player.h" #include "ScriptedCreature.h" #include "TaskScheduler.h" +#include "PassiveAI.h" #include "karazhan.h" enum Spells { - // phase 1 + // Ground Phase + SPELL_RAIN_OF_BONES = 37098, + SPELL_SMOKING_BLAST = 37057, + SPELL_FIREBALL_BARRAGE = 30282, + SPELL_SEARING_CINDERS = 30127, + // Air Phase SPELL_BELLOWING_ROAR = 39427, SPELL_CLEAVE = 30131, SPELL_CHARRED_EARTH = 30129, SPELL_DISTRACTING_ASH = 30130, SPELL_SMOLDERING_BREATH = 30210, SPELL_TAIL_SWEEP = 25653, - // phase 2 - SPELL_RAIN_OF_BONES = 37098, - SPELL_SMOKING_BLAST = 37057, - SPELL_FIREBALL_BARRAGE = 30282, - SPELL_SEARING_CINDERS = 30127, SPELL_SUMMON_SKELETON = 30170 }; @@ -43,20 +44,45 @@ enum Says { EMOTE_SUMMON = 0, YELL_AGGRO = 1, - YELL_FLY_PHASE = 2, + YELL_AIR_PHASE = 2, YELL_LAND_PHASE = 3, EMOTE_BREATH = 4 }; -enum Groups +enum Actions +{ + ACTION_START_INTRO = 0 +}; + +enum Phases +{ + PHASE_INTRO = 0, + PHASE_GROUND = 1, + PHASE_FLY = 2, + PHASE_TRANSITION = 3, +}; + + enum Groups { GROUP_GROUND = 0, - GROUP_FLYING = 1 -}; + GROUP_AIR = 1, + GROUP_LAND = 2 + }; enum Points { - POINT_DESPAWN = 10 // Other points used dynamically throughout the script + POINT_INTRO_TAKE_OFF = 11, + POINT_INTRO_PRE_LAND = 8, + POINT_INTRO_LAND = 12, + POINT_PRE_FLY_EAST = 21, + POINT_PRE_FLY_SOUTH = 22, + POINT_PRE_FLY_WEST = 23, + POINT_PRE_FLY = 24, + POINT_FLY = 31, + POINT_LANDING_PRE = 41, + POINT_LANDING_WEST = 42, + POINT_PRE_LAND = 5, + POINT_LAND = 51, }; float IntroWay[8][3] = @@ -71,13 +97,20 @@ float IntroWay[8][3] = {-11163.00f, -1903.00f, 91.473f} }; //TODO: move to table +Position const homePos = {-11003.7f, -1760.19f, 140.253f}; +Position const introLandPos = {-11142.712f, -1891.193f, 92.25038f}; +Position const preFlySouthPos = {-11193.77f, -1921.983f, 107.9845f}; +Position const preFlyEastPos = {-11167.065f, -1976.3473f, 109.91183f}; +Position const preFlyWestPos = {-11095.48f, -1866.5396f, 107.868996}; +Position const preFlyPos = {-11154.900391f, -1850.670044f, 103.264999f}; +Position const flyPos = {-11160.125f, -1870.683f, 97.73876f}; +Position const landPos = {-11162.231f, -1900.3287f, 91.47627f}; + struct boss_nightbane : public BossAI { boss_nightbane(Creature* creature) : BossAI(creature, DATA_NIGHTBANE) { - _intro = true; _skeletonCount = 5; - _movePhase = 0; } void Reset() override @@ -85,33 +118,37 @@ struct boss_nightbane : public BossAI BossAI::Reset(); _skeletonscheduler.CancelAll(); - me->SetSpeed(MOVE_RUN, 2.0f); - me->SetDisableGravity(_intro); - me->SetWalk(false); - me->setActive(true); + _triggerCountTakeOffWhileFlying = 0; + _airPhasesCompleted = 0; - _flying = false; - _movement = false; - _intro = true; - Phase = 1; - _movePhase = 0; + me->SetSpeed(MOVE_RUN, me->GetCreatureTemplate()->speed_run); + me->SetCanFly(true); + me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_PASSIVE); ScheduleHealthCheckEvent({ 75, 50, 25 }, [&]{ - TakeOff(); + TriggerHealthTakeOff(); }); } + void JustReachedHome() override + { + BossAI::JustReachedHome(); + me->DespawnOnEvade(); + } + void EnterEvadeMode(EvadeReason why) override { - BossAI::EnterEvadeMode(why); + me->SetHomePosition(homePos); + me->SetCanFly(true); me->SetDisableGravity(true); me->SendMovementFlagUpdate(); - me->GetMotionMaster()->MoveTakeoff(POINT_DESPAWN, -11013.246f, -1770.5212f, 166.50139f); + BossAI::EnterEvadeMode(why); } void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageEffectType, SpellSchoolMask spellSchoolMask) override { - if (_flying || Phase == 2) + if (_airPhasesCompleted < 3) { if (damage >= me->GetHealth()) { @@ -124,12 +161,31 @@ struct boss_nightbane : public BossAI void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); - _intro = false; - Talk(YELL_AGGRO); ScheduleGround(); } + void DoAction(int32 action) override + { + if (action == ACTION_START_INTRO) + { + me->GetMap()->LoadGrid(-11260.0f, -1771.0f); // load grid at far end of intro path + me->GetMap()->SetVisibilityRange(DEFAULT_VISIBILITY_INSTANCE + 100.0f); // see nightbane + me->AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); + _phase = PHASE_INTRO; + Talk(EMOTE_SUMMON); + scheduler.Schedule(2s, [this](TaskContext /*context*/) + { + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetDisableGravity(true); + me->GetMotionMaster()->MoveTakeoff(POINT_INTRO_TAKE_OFF, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 10.0f, 13.99879f); + }).Schedule(4s, [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MovePath(me->GetEntry()*10, false); + }); + } + } + void ScheduleGround() { scheduler.Schedule(30s, GROUP_GROUND, [this](TaskContext context) @@ -169,12 +225,13 @@ struct boss_nightbane : public BossAI { _skeletonSpawnCounter = 0; - scheduler.Schedule(2s, GROUP_FLYING, [this](TaskContext) + scheduler.Schedule(2s, GROUP_AIR, [this](TaskContext) { DoResetThreatList(); if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f)) { _skeletonSpawnPos = target->GetPosition(); + me->SetFacingTo(_skeletonSpawnPos.GetOrientation()); me->CastSpell(_skeletonSpawnPos.GetPositionX(), _skeletonSpawnPos.GetPositionY(), _skeletonSpawnPos.GetPositionZ(), SPELL_RAIN_OF_BONES, true); _skeletonscheduler.Schedule(50ms, [this](TaskContext context) { @@ -187,18 +244,23 @@ struct boss_nightbane : public BossAI } }); } - }).Schedule(20s, GROUP_FLYING, [this](TaskContext context) + }).Schedule(20s, GROUP_AIR, [this](TaskContext context) { - DoCastRandomTarget(SPELL_DISTRACTING_ASH); + if (Unit* target = SelectTarget(SelectTargetMethod::Random)) + { + me->SetFacingToObject(target); + DoCast(target, SPELL_DISTRACTING_ASH); + } context.Repeat(2s); //timer wrong? - }).Schedule(25s, GROUP_FLYING, [this](TaskContext context) + }).Schedule(25s, GROUP_AIR, [this](TaskContext context) { //5 seconds added due to double trigger? //trigger for timer in original + in rain of bones //timers need some investigation + me->SetFacingToObject(me->GetVictim()); DoCastVictim(SPELL_SMOKING_BLAST); context.Repeat(1500ms); //timer wrong? - }).Schedule(13s, GROUP_FLYING, [this](TaskContext context) + }).Schedule(13s, GROUP_AIR, [this](TaskContext context) { DoCastOnFarAwayPlayers(SPELL_FIREBALL_BARRAGE, false, 80.0f); context.Repeat(20s); @@ -207,58 +269,135 @@ struct boss_nightbane : public BossAI void AttackStart(Unit* who) override { - if (!_intro && !_flying) + if (_phase == PHASE_GROUND) ScriptedAI::AttackStart(who); } void MoveInLineOfSight(Unit* who) override { - if (!_intro && !_flying) + if (_phase == PHASE_GROUND) ScriptedAI::MoveInLineOfSight(who); } + void PathEndReached(uint32 pathId) override + { + BossAI::PathEndReached(pathId); + if (pathId == me->GetEntry()*10) // intro + { + me->GetMap()->SetVisibilityRange(DEFAULT_VISIBILITY_INSTANCE); // restore visibility + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + me->ClearUnitState(UNIT_STATE_IGNORE_PATHFINDING); + me->GetMotionMaster()->MovePoint(POINT_INTRO_LAND, introLandPos); + me->SetSpeed(MOVE_RUN, 2.0f); + }).Schedule(3s, [this](TaskContext /*context*/) + { + me->SetDisableGravity(false); + me->SetCanFly(false); + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); + _phase = PHASE_GROUND; + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + }).Schedule(8s, [this](TaskContext /*context*/) + { + if (!SelectTargetFromPlayerList(45.0f)) + { + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + }); + } + else if (pathId == me->GetEntry()*10+1) // landing + { + _airPhasesCompleted++; + if (_triggerCountTakeOffWhileFlying > 0) + { + _triggerCountTakeOffWhileFlying--; + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MovePoint(POINT_PRE_FLY_SOUTH, preFlySouthPos); + }); + } + else + { + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + DoResetThreatList(); + me->GetMotionMaster()->MovePoint(POINT_LAND, landPos); + me->SetDisableGravity(false); + me->SetCanFly(false); + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + _phase = PHASE_GROUND; + ScheduleGround(); + }); + } + } + } + void MovementInform(uint32 type, uint32 id) override { if (type != POINT_MOTION_TYPE) return; - if (id == POINT_DESPAWN) + switch (id) { - me->DespawnOnEvade(); - } - - if (_intro) - { - if (id >= 8) - { - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); - me->SetInCombatWithZone(); - return; - } - - _movePhase = id + 1; - return; - } - - if (_flying) - { - if (id == 0) - { + case POINT_INTRO_TAKE_OFF: + break; + case POINT_INTRO_LAND: + DoStartMovement(me->GetVictim()); + break; + case POINT_PRE_FLY_EAST: + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MovePoint(POINT_PRE_FLY_SOUTH, preFlySouthPos); + }); + break; + case POINT_PRE_FLY_SOUTH: + case POINT_PRE_FLY_WEST: + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MovePoint(POINT_PRE_FLY, preFlyPos); + }); + break; + case POINT_PRE_FLY: + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MovePoint(POINT_FLY, flyPos); + }); + break; + case POINT_FLY: + _phase = PHASE_FLY; Talk(EMOTE_BREATH); - _flying = false; - Phase = 2; - return; - } - - if (id < 8) - _movePhase = id + 1; - else - { - Phase = 1; - _flying = false; - _movement = true; - return; - } + ScheduleFly(); + ScheduleLand(); + break; + case POINT_LANDING_PRE: + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MovePoint(POINT_LANDING_WEST, preFlyWestPos); + }); + break; + case POINT_LANDING_WEST: + if (_triggerCountTakeOffWhileFlying > 0) + { + _airPhasesCompleted++; + _triggerCountTakeOffWhileFlying--; + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MovePoint(POINT_PRE_FLY, preFlyPos); + }); + } + else + { + scheduler.Schedule(0s, [this](TaskContext /*context*/) + { + me->GetMotionMaster()->MovePath(me->GetEntry()*10+1, false); + }); + } + break; + case POINT_LAND: + DoStartMovement(me->GetVictim()); + break; } } @@ -284,107 +423,79 @@ struct boss_nightbane : public BossAI } } - void TakeOff() + void TriggerHealthTakeOff() { - Talk(YELL_FLY_PHASE); + if (_phase != PHASE_GROUND) + { + _triggerCountTakeOffWhileFlying++; + return; + } + _phase = PHASE_TRANSITION; + Talk(YELL_AIR_PHASE); scheduler.CancelGroup(GROUP_GROUND); - me->InterruptSpell(CURRENT_GENERIC_SPELL); me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); me->SetDisableGravity(true); + me->SetCanFly(true); + me->SendMovementFlagUpdate(); + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + FlyToClosestPreFlyWayPoint(); + } + + void FlyToClosestPreFlyWayPoint() + { + Position closestWP = preFlyPos; + if (me->GetDistance(preFlyEastPos) < me->GetDistance(closestWP)) + closestWP = preFlyEastPos; + if (me->GetDistance(preFlySouthPos) < me->GetDistance(closestWP)) + closestWP = preFlySouthPos; + if (me->GetDistance(preFlyWestPos) < me->GetDistance(closestWP)) + closestWP = preFlyWestPos; + me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MovePoint(0, IntroWay[2][0], IntroWay[2][1], IntroWay[2][2]); + if (closestWP == preFlyPos) + me->GetMotionMaster()->MovePoint(POINT_PRE_FLY, closestWP); + else if (closestWP == preFlyEastPos) + me->GetMotionMaster()->MovePoint(POINT_PRE_FLY_EAST, closestWP); + else if (closestWP == preFlySouthPos) + me->GetMotionMaster()->MovePoint(POINT_PRE_FLY_SOUTH, closestWP); + else if (closestWP == preFlyWestPos) + me->GetMotionMaster()->MovePoint(POINT_PRE_FLY_WEST, closestWP); + } - _flying = true; - - ScheduleFly(); - - //handle landing again - scheduler.Schedule(45s, 60s, [this](TaskContext) + void ScheduleLand() + { + scheduler.Schedule(30s, GROUP_LAND, [this](TaskContext) /*context*/ { Talk(YELL_LAND_PHASE); + scheduler.CancelGroup(GROUP_AIR); + _phase = PHASE_TRANSITION; me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MovePoint(3, IntroWay[3][0], IntroWay[3][1], IntroWay[3][2]); - - _flying = true; - scheduler.CancelGroup(GROUP_FLYING); - scheduler.Schedule(2s, [this](TaskContext) - { - ScheduleGround(); - }); + me->GetMotionMaster()->MovePoint(POINT_LANDING_PRE, preFlyPos); }); } void UpdateAI(uint32 diff) override { - if (_intro) - { - if (_movePhase) - { - if (_movePhase >= 7) - { - me->SetDisableGravity(false); - me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); - me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]); - } - else - { - me->GetMotionMaster()->MovePoint(_movePhase, IntroWay[_movePhase][0], IntroWay[_movePhase][1], IntroWay[_movePhase][2]); - } - _movePhase = 0; - } - return; - } - - if (_flying && _movePhase) - { - if (_movePhase >= 7) - { - me->SetDisableGravity(false); - DoResetThreatList(); - me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); - me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); - me->GetMotionMaster()->MovePoint(8, IntroWay[7][0], IntroWay[7][1], IntroWay[7][2]); - } - else - me->GetMotionMaster()->MovePoint(_movePhase, IntroWay[_movePhase][0], IntroWay[_movePhase][1], IntroWay[_movePhase][2]); - - _movePhase = 0; - } + scheduler.Update(diff); + _skeletonscheduler.Update(diff); if (!UpdateVictim()) return; - if (_flying) - return; - - scheduler.Update(diff); - _skeletonscheduler.Update(diff); - - // Phase 1 "GROUND FIGHT" - if (Phase == 1) + if (_phase == PHASE_GROUND) { - if (_movement) - { - DoStartMovement(me->GetVictim()); - _movement = false; - } - DoMeleeAttackIfReady(); } } private: - uint32 Phase; + uint8 _phase; + uint8 _airPhasesCompleted; + uint8 _triggerCountTakeOffWhileFlying; TaskScheduler _skeletonscheduler; - - bool _intro; - bool _flying; - bool _movement; - - uint32 _movePhase; uint8 _skeletonCount; uint8 _skeletonSpawnCounter; Position _skeletonSpawnPos; @@ -399,22 +510,31 @@ public: { if (InstanceScript* instance = go->GetInstanceScript()) { - if (instance->GetData(DATA_NIGHTBANE) != DONE && !go->FindNearestCreature(NPC_NIGHTBANE, 40.0f)) + // if (instance->GetBossState(DATA_NIGHTBANE) == NOT_STARTED || instance->GetBossState(DATA_NIGHTBANE) == FAIL) + if (instance->GetBossState(DATA_NIGHTBANE) == NOT_STARTED) { if (Creature* nightbane = instance->GetCreature(DATA_NIGHTBANE)) { - nightbane->GetMotionMaster()->MovePoint(0, IntroWay[0][0], IntroWay[0][1], IntroWay[0][2]); - nightbane->AI()->Talk(EMOTE_SUMMON); + if (nightbane->IsAlive()) + { + nightbane->AI()->DoAction(ACTION_START_INTRO); + return true; + } } } } - return false; } }; +struct npc_nightbane_helper_target : public NullCreatureAI +{ + npc_nightbane_helper_target(Creature* creature) : NullCreatureAI(creature) { me->SetDisableGravity(true); } +}; + void AddSC_boss_nightbane() { RegisterKarazhanCreatureAI(boss_nightbane); new go_blackened_urn(); + RegisterKarazhanCreatureAI(npc_nightbane_helper_target); }