diff --git a/data/sql/updates/pending_db_world/rev_1635867595421095400.sql b/data/sql/updates/pending_db_world/rev_1635867595421095400.sql new file mode 100644 index 000000000..1b92713db --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1635867595421095400.sql @@ -0,0 +1,35 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1635867595421095400'); + +SET @PATH:= 137969 * 10; +DELETE FROM `waypoint_data` WHERE `id` = @PATH; +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES +(@PATH, 1 , 117.894, -560.9407, 107.8397, 0, 0, 1, 0, 100, 0), +(@PATH, 2 , 104.644, -557.4407, 108.0897, 0, 0, 1, 0, 100, 0), +(@PATH, 3 , 99.9641, -555.8882, 109.5087, 0, 0, 1, 0, 100, 0), +(@PATH, 4 , 96.7141, -554.3882, 110.7587, 0, 0, 1, 0, 100, 0), +(@PATH, 5 , 95.2141, -553.8882, 110.7587, 0, 0, 1, 0, 100, 0), +(@PATH, 6 , 91.2141, -552.1382, 110.7587, 0, 0, 1, 0, 100, 0), +(@PATH, 7 , 89.7141, -551.3882, 111.0087, 0, 0, 1, 0, 100, 0), +(@PATH, 8 , 87.9641, -550.6382, 111.0087, 0, 0, 1, 0, 100, 0), +(@PATH, 9 , 89.5555, -551.2523, 111.0189, 0, 0, 1, 0, 100, 0), +(@PATH, 10, 87.8055, -550.5023, 111.0189, 0, 0, 1, 0, 100, 0), +(@PATH, 11, 82.5555, -548.2523, 111.0189, 0, 0, 1, 0, 100, 0), +(@PATH, 12, 55.5090, -534.9372, 110.9415, 0, 0, 1, 0, 100, 0), +(@PATH, 13, 27.8879, -513.8772, 110.9468, 0, 0, 1, 0, 100, 0), +(@PATH, 14, 17.0559, -545.8477, 110.9305, 0, 0, 1, 0, 100, 0), +(@PATH, 15, 74.8466, -549.6145, 110.9279, 0, 0, 1, 0, 100, 0), +(@PATH, 16, 70.5093, -524.7200, 110.9333, 0, 0, 1, 0, 100, 0), +(@PATH, 17, 78.7893, -549.1487, 110.9274, 0, 0, 1, 0, 100, 0); + +DELETE FROM `areatrigger_scripts` WHERE `entry` IN (2066, 2067); +INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES +(2066, 'at_trigger_the_beast_movement'), +(2067, 'at_the_beast_room'); + +DELETE FROM `creature_text` WHERE `CreatureID` IN (10317, 10776); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(10317, 0, 0, "We're doomed!", 14, 0, 100, 0, 0, 0, 5622, 0, 'Blackhand elite'), +(10776, 0, 0, "Leaping leper gnomes! I've been stuck in there for months. Thanks, gang.", 12, 0, 100, 0, 0, 0, 6066, 0, 'Finkle Einhorn'); + +-- Its not perm spawn, its dynamic depending if the beast has been skinned or not +DELETE FROM `creature` WHERE `guid` = 42637; diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h index 3a39ee070..2fc63e865 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h @@ -72,7 +72,8 @@ enum CreaturesIds NPC_BLACKHAND_SUMMONER = 9818, NPC_BLACKHAND_VETERAN = 9819, NPC_BLACKHAND_INCARCERATOR = 10316, - NPC_LORD_VICTOR_NEFARIUS = 10162 + NPC_LORD_VICTOR_NEFARIUS = 10162, + NPC_FINKLE_EINHORN = 10776 }; enum AdditionalData @@ -82,7 +83,8 @@ enum AdditionalData EVENT_PYROGUARD_EMBERSEER = 4884, AREATRIGGER = 1, AREATRIGGER_DRAGONSPIRE_HALL = 2046, - AREATRIGGER_BLACKROCK_STADIUM = 2026 + AREATRIGGER_BLACKROCK_STADIUM = 2026, + SAY_FINKLE_GANG = 0 }; enum GameObjectsIds diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp index 0582fc296..0f296262f 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_the_beast.cpp @@ -16,21 +16,75 @@ */ #include "blackrock_spire.h" +#include "Player.h" #include "ScriptedCreature.h" #include "ScriptMgr.h" enum Spells { SPELL_FLAMEBREAK = 16785, - SPELL_IMMOLATE = 20294, + SPELL_IMMOLATE = 15570, SPELL_TERRIFYINGROAR = 14100, + SPELL_BERSERKER_CHARGE = 16636, + SPELL_FIREBALL = 16788, + SPELL_FIREBLAST = 16144, + SPELL_SUICIDE = 8329 }; enum Events { - EVENT_FLAME_BREAK = 1, - EVENT_IMMOLATE = 2, - EVENT_TERRIFYING_ROAR = 3, + EVENT_FLAME_BREAK = 1, + EVENT_IMMOLATE = 2, + EVENT_TERRIFYING_ROAR = 3, + EVENT_BERSERKER_CHARGE = 4, + EVENT_FIREBALL = 5, + EVENT_FIREBLAST = 6 +}; + +enum BeastMisc +{ + DATA_BEAST_REACHED = 1, + DATA_BEAST_ROOM = 2, + BEAST_MOVEMENT_ID = 1379690, + + NPC_BLACKHAND_ELITE = 10317, + + SAY_BLACKHAND_DOOMED = 0 +}; + +Position const OrcsRunawayPosition = { 34.163567f, -536.852356f, 110.935196f, 6.056306f }; + +class OrcMoveEvent : public BasicEvent +{ +public: + OrcMoveEvent(Creature* me) : _me(me) {} + + bool Execute(uint64 /*time*/, uint32 /*diff*/) override + { + _me->SetReactState(REACT_PASSIVE); + Position movePos; + _me->GetRandomPoint(OrcsRunawayPosition, 10.0f, movePos); + _me->GetMotionMaster()->MovePoint(1, movePos); + return true; + } + +private: + Creature* _me; +}; + +class OrcDeathEvent : public BasicEvent +{ +public: + OrcDeathEvent(Creature* me) : _me(me) { } + + bool Execute(uint64 /*time*/, uint32 /*diff*/) override + { + _me->CastSpell(_me, SPELL_SUICIDE, true); + return true; + } + +private: + Creature* _me; }; class boss_the_beast : public CreatureScript @@ -45,35 +99,98 @@ public: struct boss_thebeastAI : public BossAI { - boss_thebeastAI(Creature* creature) : BossAI(creature, DATA_THE_BEAST) { } + boss_thebeastAI(Creature* creature) : BossAI(creature, DATA_THE_BEAST), _beastReached(false), _orcYelled(false) {} void Reset() override { _Reset(); + + if (_beastReached) + { + me->GetMotionMaster()->MovePath(BEAST_MOVEMENT_ID, true); + } } void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - events.ScheduleEvent(EVENT_FLAME_BREAK, 12 * IN_MILLISECONDS); - events.ScheduleEvent(EVENT_IMMOLATE, 3 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_FLAME_BREAK, 12 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_IMMOLATE, 3 * IN_MILLISECONDS); events.ScheduleEvent(EVENT_TERRIFYING_ROAR, 23 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_BERSERKER_CHARGE, 2 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_FIREBALL, 8 * IN_MILLISECONDS, 21 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_FIREBLAST, 5 * IN_MILLISECONDS, 8 * IN_MILLISECONDS); } - void JustDied(Unit* /*killer*/) override + void SetData(uint32 type, uint32 /*data*/) override { - _JustDied(); + switch (type) + { + case DATA_BEAST_ROOM: + { + if (!_orcYelled) + { + if (_nearbyOrcsGUIDs.empty()) + { + FindNearbyOrcs(); + } + + //! vector still empty, creatures are missing + if (_nearbyOrcsGUIDs.empty()) + { + return; + } + + _orcYelled = true; + + bool yelled = false; + for (ObjectGuid guid : _nearbyOrcsGUIDs) + { + if (Creature* orc = ObjectAccessor::GetCreature(*me, guid)) + { + if (!yelled) + { + yelled = true; + orc->AI()->Talk(SAY_BLACKHAND_DOOMED); + } + + orc->m_Events.AddEvent(new OrcMoveEvent(orc), me->m_Events.CalculateTime(3 * IN_MILLISECONDS)); + orc->m_Events.AddEvent(new OrcDeathEvent(orc), me->m_Events.CalculateTime(9 * IN_MILLISECONDS)); + } + } + } + break; + } + case DATA_BEAST_REACHED: + { + if (!_beastReached) + { + _beastReached = true; + me->GetMotionMaster()->MovePath(BEAST_MOVEMENT_ID, true); + + // There is a chance player logged in between areatriggers (realm crash or restart) + // executing part of script which happens when player enters boss room + // otherwise we will see weird behaviour when someone steps on the previous areatrigger (dead mob yelling/moving) + SetData(DATA_BEAST_ROOM, DATA_BEAST_ROOM); + } + break; + } + } } void UpdateAI(uint32 diff) override { if (!UpdateVictim()) + { return; + } events.Update(diff); if (me->HasUnitState(UNIT_STATE_CASTING)) + { return; + } while (uint32 eventId = events.ExecuteEvent()) { @@ -85,21 +202,117 @@ public: break; case EVENT_IMMOLATE: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) + { DoCast(target, SPELL_IMMOLATE); + } events.ScheduleEvent(EVENT_IMMOLATE, 8 * IN_MILLISECONDS); break; case EVENT_TERRIFYING_ROAR: DoCastVictim(SPELL_TERRIFYINGROAR); events.ScheduleEvent(EVENT_TERRIFYING_ROAR, 20 * IN_MILLISECONDS); break; + case EVENT_BERSERKER_CHARGE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 38.f, true)) + { + DoCast(target, SPELL_BERSERKER_CHARGE); + } + events.ScheduleEvent(EVENT_BERSERKER_CHARGE, 15 * IN_MILLISECONDS, 23 * IN_MILLISECONDS); + break; + case EVENT_FIREBALL: + DoCastVictim(SPELL_FIREBALL); + events.ScheduleEvent(EVENT_FIREBALL, 8 * IN_MILLISECONDS, 21 * IN_MILLISECONDS); + break; + case EVENT_FIREBLAST: + DoCastVictim(SPELL_FIREBLAST); + events.ScheduleEvent(EVENT_FIREBLAST, 5 * IN_MILLISECONDS, 8 * IN_MILLISECONDS); + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + return; } } + DoMeleeAttackIfReady(); } + + void FindNearbyOrcs() + { + std::list temp; + me->GetCreatureListWithEntryInGrid(temp, NPC_BLACKHAND_ELITE, 50.0f); + for (Creature* creature : temp) + { + if (creature->IsAlive()) + { + _nearbyOrcsGUIDs.push_back(creature->GetGUID()); + } + } + } + + private: + bool _beastReached; + bool _orcYelled; + GuidVector _nearbyOrcsGUIDs; }; }; +//! The beast room areatrigger, this one triggers boss pathing. (AT Id 2066) +class at_trigger_the_beast_movement : public AreaTriggerScript +{ +public: + at_trigger_the_beast_movement() : AreaTriggerScript("at_trigger_the_beast_movement") { } + + bool OnTrigger(Player* player, AreaTrigger const* /*at*/) override + { + if (player->IsGameMaster()) + { + return false; + } + + if (InstanceScript* instance = player->GetInstanceScript()) + { + if (Creature* beast = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THE_BEAST))) + { + beast->AI()->SetData(DATA_BEAST_REACHED, DATA_BEAST_REACHED); + } + + return true; + } + + return false; + } +}; + +class at_the_beast_room : public AreaTriggerScript +{ +public: + at_the_beast_room() : AreaTriggerScript("at_the_beast_room") { } + + bool OnTrigger(Player* player, AreaTrigger const* /*at*/) override + { + if (player->IsGameMaster()) + { + return false; + } + + if (InstanceScript* instance = player->GetInstanceScript()) + { + if (Creature* beast = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THE_BEAST))) + { + beast->AI()->SetData(DATA_BEAST_ROOM, DATA_BEAST_ROOM); + } + + return true; + } + + return false; + } +}; + void AddSC_boss_thebeast() { new boss_the_beast(); + new at_trigger_the_beast_movement(); + new at_the_beast_room(); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp index 74289cbe1..6ce09c80f 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp @@ -131,6 +131,9 @@ public: if (GetBossState(DATA_GYTH) == DONE) creature->DisappearAndDie(); break; + case NPC_FINKLE_EINHORN: + creature->AI()->Talk(SAY_FINKLE_GANG); + break; } }