diff options
author | Jason Dove <1695733+jasongdove@users.noreply.github.com> | 2025-05-30 04:04:07 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-05-30 11:04:07 +0200 |
commit | a50e1c7ab3f6cdbb60509fab82c00a8eb6eb6f7d (patch) | |
tree | 1222391f1e09259c6677e21c54356bb356f137ba | |
parent | e39dddec0b805db4f11e08dcf7e22e0f003ca0e1 (diff) |
Scripts/RedridgeMountains: Implement Quest "Saving Foreman Oslow" (#30947)
-rw-r--r-- | sql/updates/world/master/2025_05_30_01_world.sql | 122 | ||||
-rw-r--r-- | src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp | 689 |
2 files changed, 682 insertions, 129 deletions
diff --git a/sql/updates/world/master/2025_05_30_01_world.sql b/sql/updates/world/master/2025_05_30_01_world.sql new file mode 100644 index 00000000000..564f37145fd --- /dev/null +++ b/sql/updates/world/master/2025_05_30_01_world.sql @@ -0,0 +1,122 @@ +# fix respawn times for bridge workers, foreman oslow, and huge boulder +UPDATE `creature` SET `spawntimesecs` = 30 WHERE `guid` IN (334925, 334930, 334929, 334928, 334931, 334927, 334926, 334924); + +# move script from alex to boulder +UPDATE `creature_template` SET `ScriptName`='npc_redridge_huge_boulder' WHERE `entry` = 43196; -- Huge Boulder +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry` = 653; -- Bridge Worker Alex + +UPDATE `creature_template` SET `unit_flags2`=0x800 WHERE `entry`=43094; -- Canyon Ettin +UPDATE `creature_template` SET `faction`=35, `unit_flags`=0x40, `VehicleId`=938, `ScriptName`='npc_redridge_subdued_canyon_ettin' WHERE `entry`=43197; -- Subdued Canyon Ettin +UPDATE `creature_template` SET `unit_flags3`=0x1000000 WHERE `entry`=43196; -- Huge Boulder + +DELETE FROM `creature_template_addon` WHERE `entry` IN (43197 /*43197 (Subdued Canyon Ettin) - Detect: Quest Invis Zone 1*/); +INSERT INTO `creature_template_addon` (`entry`, `PathId`, `mount`, `StandState`, `AnimTier`, `VisFlags`, `SheathState`, `PvpFlags`, `emote`, `aiAnimKit`, `movementAnimKit`, `meleeAnimKit`, `visibilityDistanceType`, `auras`) VALUES +(43197, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, '81299'); -- 43197 (Subdued Canyon Ettin) - Detect: Quest Invis Zone 1 + +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=341 AND `DifficultyID`=0); -- 341 (Foreman Oslow) - +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=648 AND `DifficultyID`=0); -- 648 (Bridge Worker Trent) - +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=649 AND `DifficultyID`=0); -- 649 (Bridge Worker Dmitri) - +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=650 AND `DifficultyID`=0); -- 650 (Bridge Worker Jess) - +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=651 AND `DifficultyID`=0); -- 651 (Bridge Worker Daniel) - +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=652 AND `DifficultyID`=0); -- 652 (Bridge Worker Matthew) - +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=653 AND `DifficultyID`=0); -- 653 (Bridge Worker Alex) - +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=43094 AND `DifficultyID`=0); -- 43094 (Canyon Ettin) - +UPDATE `creature_template_difficulty` SET `StaticFlags1`=0x0, `VerifiedBuild`=60822 WHERE (`Entry`=43197 AND `DifficultyID`=0); -- 43197 (Subdued Canyon Ettin) - + +DELETE FROM `spell_script_names` WHERE `spell_id` IN (80704, 80702, 80739, 80707); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(80704, 'spell_redridge_control_ettin'), +(80702, 'spell_redridge_control_ettin_2'), +(80739, 'spell_gen_reverse_cast_target_to_caster_triggered'), +(80707, 'spell_gen_despawn_target'); + +# exit location for thrown boulder +DELETE FROM `vehicle_seat_addon` WHERE `SeatEntry` = 8161; +INSERT INTO `vehicle_seat_addon` (`SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue`) VALUES +(8161, 0, -9310.855, -2366.367, 67.17842, 0, 2); + +DELETE FROM `creature_text` WHERE `CreatureID` = 43197 AND `GroupID` IN (0, 1, 2, 3); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(43197, 0, 0, 'ROCK NOT SO HEAVY! PUNY HUMIES!', 12, 0, 100, 0, 0, 0, 43218, 0, 'Subdued Canyon Ettin to Huge Boulder'), +(43197, 1, 0, 'Where trow? TROW ON BRIDGE??', 12, 0, 100, 0, 0, 0, 43220, 0, 'Subdued Canyon Ettin to Huge Boulder'), +(43197, 2, 0, 'OK! Me trow in water!', 12, 0, 100, 0, 0, 0, 43222, 0, 'Subdued Canyon Ettin to Huge Boulder'), +(43197, 3, 0, 'BYE, BYE, DADDY!', 14, 0, 100, 0, 0, 0, 43226, 0, 'Subdued Canyon Ettin to Huge Boulder'); + +DELETE FROM `creature_text` WHERE `CreatureID` = 648 AND `GroupID` = 1; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(648, 1, 0, 'NO, NO, NO! Please!', 12, 0, 100, 0, 0, 0, 43230, 0, 'Bridge Worker Trent - Scared'), +(648, 1, 1, 'NOOOOOO!!!', 12, 0, 100, 0, 0, 0, 43231, 0, 'Bridge Worker Trent - Scared'), +(648, 1, 2, 'ANYWHERE BUT THE BRIDGE!', 12, 0, 100, 0, 0, 0, 43232, 0, 'Bridge Worker Trent - Scared'); + +DELETE FROM `creature_text` WHERE `CreatureID` = 649 AND `GroupID` = 1; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(649, 1, 0, 'NO, NO, NO! Please!', 12, 0, 100, 0, 0, 0, 43230, 0, 'Bridge Worker Dmitri - Scared'), +(649, 1, 1, 'NOOOOOO!!!', 12, 0, 100, 0, 0, 0, 43231, 0, 'Bridge Worker Dmitri - Scared'), +(649, 1, 2, 'ANYWHERE BUT THE BRIDGE!', 12, 0, 100, 0, 0, 0, 43232, 0, 'Bridge Worker Dmitri - Scared'); + +DELETE FROM `creature_text` WHERE `CreatureID` = 650 AND `GroupID` = 1; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(650, 1, 0, 'NO, NO, NO! Please!', 12, 0, 100, 0, 0, 0, 43230, 0, 'Bridge Worker Jess - Scared'), +(650, 1, 1, 'NOOOOOO!!!', 12, 0, 100, 0, 0, 0, 43231, 0, 'Bridge Worker Jess - Scared'), +(650, 1, 2, 'ANYWHERE BUT THE BRIDGE!', 12, 0, 100, 0, 0, 0, 43232, 0, 'Bridge Worker Jess - Scared'); + +DELETE FROM `creature_text` WHERE `CreatureID` = 651 AND `GroupID` = 1; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(651, 1, 0, 'NO, NO, NO! Please!', 12, 0, 100, 0, 0, 0, 43230, 0, 'Bridge Worker Daniel - Scared'), +(651, 1, 1, 'NOOOOOO!!!', 12, 0, 100, 0, 0, 0, 43231, 0, 'Bridge Worker Daniel - Scared'), +(651, 1, 2, 'ANYWHERE BUT THE BRIDGE!', 12, 0, 100, 0, 0, 0, 43232, 0, 'Bridge Worker Daniel - Scared'); + +DELETE FROM `creature_text` WHERE `CreatureID` = 652 AND `GroupID` = 2; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(652, 2, 0, 'NO, NO, NO! Please!', 12, 0, 100, 0, 0, 0, 43230, 0, 'Bridge Worker Matthew - Scared'), +(652, 2, 1, 'NOOOOOO!!!', 12, 0, 100, 0, 0, 0, 43231, 0, 'Bridge Worker Matthew - Scared'), +(652, 2, 2, 'ANYWHERE BUT THE BRIDGE!', 12, 0, 100, 0, 0, 0, 43232, 0, 'Bridge Worker Matthew - Scared'); + +DELETE FROM `creature_text` WHERE `CreatureID` = 653 AND `GroupID` = 5; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(653, 5, 0, 'NO, NO, NO! Please!', 12, 0, 100, 0, 0, 0, 43230, 0, 'Bridge Worker Alex - Scared'), +(653, 5, 1, 'NOOOOOO!!!', 12, 0, 100, 0, 0, 0, 43231, 0, 'Bridge Worker Alex - Scared'), +(653, 5, 2, 'ANYWHERE BUT THE BRIDGE!', 12, 0, 100, 0, 0, 0, 43232, 0, 'Bridge Worker Alex - Scared'); + +-- Path 1 for Subdued Canyon Ettin +SET @ENTRY := 43197; +SET @PATHOFFSET := 0; +SET @PATH := @ENTRY * 100 + @PATHOFFSET; +DELETE FROM `waypoint_path` WHERE `PathId`= @PATH; +INSERT INTO `waypoint_path` (`PathId`, `MoveType`, `Flags`, `Velocity`, `Comment`) VALUES +(@PATH, 1, 0x0, NULL, 'Subdued Canyon Ettin - Scripted Path - Path To Water'); + +DELETE FROM `waypoint_path_node` WHERE `PathId`= @PATH; +INSERT INTO `waypoint_path_node` (`PathId`, `NodeId`, `PositionX`, `PositionY`, `PositionZ`, `Orientation`, `Delay`) VALUES +(@PATH, 0, -9270.707, -2299.5933, 69.642654, NULL, 0), +(@PATH, 1, -9271.457, -2309.5933, 69.892654, NULL, 0), +(@PATH, 2, -9279.707, -2319.0933, 66.892654, NULL, 0), +(@PATH, 3, -9303.957, -2330.5933, 61.642654, NULL, 0), +(@PATH, 4, -9324.86, -2338.94, 61.2445, 5.0614, 0); + +-- Path 2 for Subdued Canyon Ettin +SET @ENTRY := 43197; +SET @PATHOFFSET := 1; +SET @PATH := @ENTRY * 100 + @PATHOFFSET; +DELETE FROM `waypoint_path` WHERE `PathId`= @PATH; +INSERT INTO `waypoint_path` (`PathId`, `MoveType`, `Flags`, `Velocity`, `Comment`) VALUES +(@PATH, 1, 0x0, NULL, 'Subdued Canyon Ettin - Scripted Path - Path Up Hill'); + +DELETE FROM `waypoint_path_node` WHERE `PathId`= @PATH; +INSERT INTO `waypoint_path_node` (`PathId`, `NodeId`, `PositionX`, `PositionY`, `PositionZ`, `Orientation`, `Delay`) VALUES +(@PATH, 0, -9308.439, -2330.975, 61.716003, NULL, 0), +(@PATH, 1, -9292.939, -2325.475, 62.716003, NULL, 0), +(@PATH, 2, -9276.939, -2316.725, 68.966, NULL, 0), +(@PATH, 3, -9260.689, -2312.725, 75.216, NULL, 0), +(@PATH, 4, -9237.689, -2313.225, 78.716, NULL, 0), +(@PATH, 5, -9214.52, -2334.01, 83.6875, NULL, 0); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceEntry` IN (80739)); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `ConditionStringValue1`, `NegativeCondition`, `Comment`) VALUES +(13, 1, 80739, 0, 0, 51, 0, 5, 43196, 0, '', 0, 'Potential target of the spell is TYPEID_UNIT, entry is 43196'); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceEntry` IN (80704)); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `ConditionStringValue1`, `NegativeCondition`, `Comment`) VALUES +(13, 2, 80704, 0, 0, 51, 0, 5, 43197, 0, '', 0, 'Potential target of the spell is TYPEID_UNIT, entry is 43197'), +(13, 2, 80704, 0, 0, 33, 0, 1, 3, 0, '', 0, 'Potential target of the spell is TYPEID_UNIT, entry is 43197, if owned by spell caster'), +(13, 2, 80704, 0, 1, 51, 0, 5, 43094, 0, '', 0, 'Otherwise, potential target of the spell is TYPEID_UNIT, entry is 43094'); diff --git a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp index 19db563152d..299cd527fec 100644 --- a/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp +++ b/src/server/scripts/EasternKingdoms/zone_redridge_mountains.cpp @@ -22,17 +22,82 @@ SDComment: Script Data End */ #include "ScriptMgr.h" -#include "ScriptedCreature.h" +#include "Containers.h" +#include "G3DPosition.hpp" #include "MotionMaster.h" +#include "MoveSplineInit.h" #include "ObjectAccessor.h" +#include "Player.h" +#include "ScriptedCreature.h" +#include "SpellAuras.h" +#include "SpellInfo.h" +#include "SpellScript.h" +#include "TemporarySummon.h" #include "WaypointDefines.h" +#include "World.h" +#include <algorithm> -enum DumpyKeeshan +enum RedridgeSpellData { - NPC_BIGEARL = 43248, + SPELL_APPLY_QUEST_INVIS_ZONE_3 = 80815, // Used by npc's in Lakeshire Inn + SPELL_APPLY_QUEST_INVIS_ZONE_4 = 80816, // Used by npc's in Lakeshire Inn + + SPELL_APPLY_QUEST_INVIS_ZONE_19 = 82099, // Used by npc's in Lakeshire Townhall + + SPELL_CONTROL_ETTIN = 80704, + SPELL_CONTROL_ETTIN_2 = 80702, + SPELL_LIFT_HUGE_BOULDER = 80739, + SPELL_LIFT_BOULDER_RIDE = 82566, + SPELL_CANYON_ETTIN_SPAWN_SPELL = 82558, + SPELL_BOULDER_AURA = 82556, + SPELL_DESPAWN_KILL_CREDIT = 228623, + SPELL_EJECT_PASSENGER_1 = 80743, + SPELL_CANYON_ETTIN_DESPAWN = 82561, + SPELL_DESPAWN_ETTIN = 80707, +}; - SPELL_APPLY_QUEST_INVIS_ZONE_3 = 80815, // Used by npc's in Lakeshire Inn - SPELL_APPLY_QUEST_INVIS_ZONE_4 = 80816 // Used by npc's in Lakeshire Inn +enum RedridgeCreatureData +{ + NPC_FOREMAN_OSLOW = 341, + NPC_BRIDGE_WORKER_ALEX = 653, + NPC_BRIDGE_WORKER_TRENT = 648, + NPC_BRIDGE_WORKER_DMITRI = 649, + NPC_BRIDGE_WORKER_JESS = 650, + NPC_BRIDGE_WORKER_DANIEL = 651, + NPC_BRIDGE_WORKER_MATTHEW = 652, + NPC_CANYON_ETTIN = 43094, + NPC_SUBDUED_CANYON_ETTIN = 43197, + NPC_HUGE_BOULDER = 43196, + + NPC_BIGEARL = 43248, +}; + +enum RedridgeTalks +{ + TALK_OSLOW_IDLE = 0, + + TALK_ALEX_HEAVE = 1, + TALK_ALEX_DAMN = 2, + TALK_ALEX_PUSH = 4, + TALK_ALEX_SCARED = 5, + + TALK_WORKERS_HO = 0, + + TALK_MATTHEW_IM_PUSHING = 1, + TALK_MATTHEW_SCARED = 2, + + TALK_TRENT_SCARED = 1, + + TALK_DMITRI_SCARED = 1, + + TALK_JESS_SCARED = 1, + + TALK_DANIEL_SCARED = 1, + + TALK_NOT_SO_HEAVY = 0, + TALK_WHERE_THROW = 1, + TALK_THROW_IN_WATER = 2, + TALK_BYE = 3 }; /*###### @@ -112,148 +177,513 @@ public: } }; -/*###### -# npc_bridge_worker_alex "Used by entry 653" "Script not fully complete. Needs scripting added for move rock will be updated in future." -######*/ - -enum BridgeWorkerAlex +enum RedridgeHugeBoulder { - EVENT_STORE_GUIDS = 1, - EVENT_OSLOW_SAY = 2, - EVENT_ALEX_SAY = 3, - EVENT_WORKERS_SAY = 4, - EVENT_ALEX_SAY_PUSH = 5, - EVENT_MATTHEW_SAY = 6, - - NPC_OSLOW = 341, - NPC_TRENT = 648, - NPC_DIMITRI = 649, - NPC_JESS = 650, - NPC_DANIEL = 651, - NPC_MATTHEW = 652, - - SAY_OSLOW = 0, - SAY_ALEX_PUT = 0, - SAY_ALEX_HEAVE = 1, - SAY_ALEX_DAMN = 2, - SAY_ALEX_HANGON = 3, - SAY_ALEX_PUSH = 4, - SAY_WORKERS_HO = 0, - SAY_MATTHEW_IM_PUSHING = 1 + EVENT_STORE_GUIDS = 1, + EVENT_OSLOW_IDLE_TALK = 2, + EVENT_ALEX_IDLE_TALK = 3, + EVENT_WORKERS_RESPONSE = 4, + EVENT_ALEX_SAY_PUSH = 5, + EVENT_MATTHEW_PUSH_RESPONSE = 6, + EVENT_REPOSITION = 7, + EVENT_BRIDGE_WORKERS_COWER = 8, + EVENT_OSLOW_GET_UP = 9, + EVENT_OSLOW_STUN = 10, + EVENT_ETTIN_LINE_1 = 11, + EVENT_ETTIN_LINE_2 = 12, + EVENT_MOVE_TO_WATER = 13, + EVENT_THROW_BOULDER = 14, + EVENT_PATH_AWAY = 15, + EVENT_DONE, + + ACTION_OSLOW_GET_UP = 0, + ACTION_COWER = 1, + ACTION_DONE, + + PATH_ETTIN_TO_WATER = 4319700, + PATH_ETTIN_UP_HILL = 4319701, + + POINT_NEAR_BOULDER = 1, + POINT_NEAR_WATER = 2, + POINT_UP_PATH = 3, }; -class npc_bridge_worker_alex : public CreatureScript +constexpr Position TrentRepositionPos = { -9281.44f, -2285.27f, 67.5123f, 6.0213f }; +constexpr Position DmitriRepositionPos = { -9282.8f, -2293.28f, 67.5089f, 6.2657f }; +constexpr Position JessRepositionPos = { -9282.27f, -2290.95f, 67.5319f, 6.0737f }; +constexpr Position DanielRepositionPos = { -9281.77f, -2287.55f, 67.5869f, 6.0911f }; +constexpr Position MatthewRepositionPos = { -9280.71f, -2283.21f, 67.5747f, 6.0737f }; +constexpr Position AlexRepositionPos = { -9279.86f, -2281.42f, 67.5854f, 5.7421f }; + +// 43196 - Huge Boulder +struct npc_redridge_huge_boulder : public CreatureAI { -public: - npc_bridge_worker_alex() : CreatureScript("npc_bridge_worker_alex") { } + npc_redridge_huge_boulder(Creature* creature) : CreatureAI(creature) { } - struct npc_bridge_worker_alexAI : public ScriptedAI + void Reset() override { - npc_bridge_worker_alexAI(Creature* creature) : ScriptedAI(creature) { } + _events.Reset(); + _oslowGUID.Clear(); + _alexGUID.Clear(); + _trentGUID.Clear(); + _dmitriGUID.Clear(); + _jessGUID.Clear(); + _danielGUID.Clear(); + _matthewGUID.Clear(); + _events.ScheduleEvent(EVENT_STORE_GUIDS, 5s); + } - void Reset() override - { - _events.Reset(); - _oslowGUID.Clear(); - _trentGUID.Clear(); - _dimitriGUID.Clear(); - _jessGUID.Clear(); - _danielGUID.Clear(); - _matthewGUID.Clear(); - _events.ScheduleEvent(EVENT_STORE_GUIDS, Seconds(5)); - _events.ScheduleEvent(EVENT_OSLOW_SAY, Seconds(40), Seconds(50)); - _events.ScheduleEvent(EVENT_ALEX_SAY, Seconds(22), Seconds(30)); - } + void SpellHit(WorldObject* /*caster*/, SpellInfo const* spell) override + { + if (spell->Id != SPELL_LIFT_BOULDER_RIDE) + return; - void UpdateAI(uint32 diff) override - { - _events.Update(diff); + _events.Reset(); + _events.ScheduleEvent(EVENT_REPOSITION, 0ms); + } - if (!UpdateVictim()) + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - while (uint32 eventId = _events.ExecuteEvent()) + case EVENT_STORE_GUIDS: { - switch (eventId) + if (Creature const* oslow = me->FindNearestCreature(NPC_FOREMAN_OSLOW, 10.0f, true)) + _oslowGUID = oslow->GetGUID(); + if (Creature const* alex = me->FindNearestCreature(NPC_BRIDGE_WORKER_ALEX, 10.0f, true)) + _alexGUID = alex->GetGUID(); + if (Creature const* trent = me->FindNearestCreature(NPC_BRIDGE_WORKER_TRENT, 10.0f, true)) + _trentGUID = trent->GetGUID(); + if (Creature const* dimitri = me->FindNearestCreature(NPC_BRIDGE_WORKER_DMITRI, 10.0f, true)) + _dmitriGUID = dimitri->GetGUID(); + if (Creature const* jess = me->FindNearestCreature(NPC_BRIDGE_WORKER_JESS, 10.0f, true)) + _jessGUID = jess->GetGUID(); + if (Creature const* daniel = me->FindNearestCreature(NPC_BRIDGE_WORKER_DANIEL, 10.0f, true)) + _danielGUID = daniel->GetGUID(); + if (Creature const* matthew = me->FindNearestCreature(NPC_BRIDGE_WORKER_MATTHEW, 10.0f, true)) + _matthewGUID = matthew->GetGUID(); + + if (!_oslowGUID || !_alexGUID || !_trentGUID || !_dmitriGUID || !_jessGUID || !_danielGUID || !_matthewGUID) + _events.Repeat(1s); + else { - case EVENT_STORE_GUIDS: - if (Creature* oslow = me->FindNearestCreature(NPC_OSLOW, 5.0f, true)) - _oslowGUID = oslow->GetGUID(); - if (Creature* trent = me->FindNearestCreature(NPC_TRENT, 5.0f, true)) - _trentGUID = trent->GetGUID(); - if (Creature* dimitri = me->FindNearestCreature(NPC_DIMITRI, 5.0f, true)) - _dimitriGUID = dimitri->GetGUID(); - if (Creature* jess = me->FindNearestCreature(NPC_JESS, 5.0f, true)) - _jessGUID = jess->GetGUID(); - if (Creature* daniel = me->FindNearestCreature(NPC_DANIEL, 5.0f, true)) - _danielGUID = daniel->GetGUID(); - if (Creature* matthew = me->FindNearestCreature(NPC_MATTHEW, 5.0f, true)) - _matthewGUID = matthew->GetGUID(); - break; - case EVENT_OSLOW_SAY: - if (Creature* oslow = ObjectAccessor::GetCreature(*me, _oslowGUID)) - oslow->AI()->Talk(SAY_OSLOW); - _events.Repeat(Seconds(40), Seconds(50)); - break; - case EVENT_ALEX_SAY: - switch (urand(0, 3)) + _events.ScheduleEvent(EVENT_OSLOW_IDLE_TALK, 45s, 60s); + _events.ScheduleEvent(EVENT_ALEX_IDLE_TALK, 20s, 30s); + } + break; + } + case EVENT_OSLOW_IDLE_TALK: + { + if (Creature const* oslow = ObjectAccessor::GetCreature(*me, _oslowGUID)) + oslow->AI()->Talk(TALK_OSLOW_IDLE); + _events.Repeat(45s, 60s); + break; + } + case EVENT_ALEX_IDLE_TALK: + { + if (Creature const* alex = ObjectAccessor::GetCreature(*me, _alexGUID)) + { + switch (uint32 text = urand(0, 3)) { - case 0: - Talk(SAY_ALEX_PUT); - break; - case 1: - Talk(SAY_ALEX_HEAVE); - _events.ScheduleEvent(EVENT_WORKERS_SAY, Seconds(2)); - break; - case 2: - Talk(SAY_ALEX_DAMN); - _events.ScheduleEvent(EVENT_ALEX_SAY_PUSH, Seconds(4)); - break; - case 3: - Talk(SAY_ALEX_HANGON); - break; + case TALK_ALEX_HEAVE: + alex->AI()->Talk(TALK_ALEX_HEAVE); + _events.ScheduleEvent(EVENT_WORKERS_RESPONSE, 1s); + break; + case TALK_ALEX_DAMN: + alex->AI()->Talk(TALK_ALEX_DAMN); + _events.ScheduleEvent(EVENT_ALEX_SAY_PUSH, 3s); + break; + default: + alex->AI()->Talk(text); + _events.Repeat(10s); + break; + } + } + break; + } + case EVENT_WORKERS_RESPONSE: + { + if (Creature const* alex = ObjectAccessor::GetCreature(*me, _alexGUID)) + { + if (Creature const* trent = ObjectAccessor::GetCreature(*me, _trentGUID)) + trent->AI()->Talk(TALK_WORKERS_HO, alex); + if (Creature const* dmitri = ObjectAccessor::GetCreature(*me, _dmitriGUID)) + dmitri->AI()->Talk(TALK_WORKERS_HO, alex); + if (Creature const* jess = ObjectAccessor::GetCreature(*me, _jessGUID)) + jess->AI()->Talk(TALK_WORKERS_HO, alex); + if (Creature const* daniel = ObjectAccessor::GetCreature(*me, _danielGUID)) + daniel->AI()->Talk(TALK_WORKERS_HO, alex); + if (Creature const* matthew = ObjectAccessor::GetCreature(*me, _matthewGUID)) + matthew->AI()->Talk(TALK_WORKERS_HO, alex); + } + _events.ScheduleEvent(EVENT_ALEX_IDLE_TALK, 20s, 30s); + break; + } + case EVENT_ALEX_SAY_PUSH: + { + if (Creature const* alex = ObjectAccessor::GetCreature(*me, _alexGUID)) + alex->AI()->Talk(TALK_ALEX_PUSH); + _events.ScheduleEvent(EVENT_MATTHEW_PUSH_RESPONSE, 4s); + break; + } + case EVENT_MATTHEW_PUSH_RESPONSE: + { + if (Creature const* matthew = ObjectAccessor::GetCreature(*me, _matthewGUID)) + { + if (Creature const* alex = ObjectAccessor::GetCreature(*me, _alexGUID)) + matthew->AI()->Talk(TALK_MATTHEW_IM_PUSHING, alex); + } + _events.ScheduleEvent(EVENT_ALEX_IDLE_TALK, 20s, 30s); + break; + } + case EVENT_REPOSITION: + { + if (Creature* trent = ObjectAccessor::GetCreature(*me, _trentGUID)) + { + trent->SetAIAnimKitId(0); + trent->GetMotionMaster()->MovePoint(0, TrentRepositionPos, true, TrentRepositionPos.GetOrientation()); + } + + if (Creature* dmitri = ObjectAccessor::GetCreature(*me, _dmitriGUID)) + { + dmitri->SetAIAnimKitId(0); + dmitri->GetMotionMaster()->MovePoint(0, DmitriRepositionPos, true, DmitriRepositionPos.GetOrientation()); + } + + if (Creature* jess = ObjectAccessor::GetCreature(*me, _jessGUID)) + { + jess->SetAIAnimKitId(0); + jess->GetMotionMaster()->MovePoint(0, JessRepositionPos, true, JessRepositionPos.GetOrientation()); + } + + if (Creature* daniel = ObjectAccessor::GetCreature(*me, _danielGUID)) + { + daniel->SetAIAnimKitId(0); + daniel->GetMotionMaster()->MovePoint(0, DanielRepositionPos, true, DanielRepositionPos.GetOrientation()); + } + + if (Creature* matthew = ObjectAccessor::GetCreature(*me, _matthewGUID)) + { + matthew->SetAIAnimKitId(0); + matthew->GetMotionMaster()->MovePoint(0, MatthewRepositionPos, true, MatthewRepositionPos.GetOrientation()); + } + + if (Creature* alex = ObjectAccessor::GetCreature(*me, _alexGUID)) + { + alex->SetAIAnimKitId(0); + alex->GetMotionMaster()->MovePoint(0, AlexRepositionPos, true, AlexRepositionPos.GetOrientation()); + } + break; + } + case EVENT_BRIDGE_WORKERS_COWER: + { + // all workers should cower + std::vector<ObjectGuid> allWorkers = { _alexGUID, _matthewGUID, _trentGUID, _dmitriGUID, _jessGUID, _danielGUID }; + for (ObjectGuid const& workerGUID : allWorkers) + if (Creature* worker = ObjectAccessor::GetCreature(*me, workerGUID)) + worker->SetEmoteState(EMOTE_STATE_COWER); + + // pick two random workers to say random scared things + Trinity::Containers::RandomResize(allWorkers, 2); + + for (ObjectGuid const workerGUID : allWorkers) + { + Creature* worker = ObjectAccessor::GetCreature(*me, workerGUID); + if (!worker) + continue; + + switch (worker->GetEntry()) + { + case NPC_BRIDGE_WORKER_ALEX: + worker->AI()->Talk(TALK_ALEX_SCARED, me); + break; + case NPC_BRIDGE_WORKER_MATTHEW: + worker->AI()->Talk(TALK_MATTHEW_SCARED, me); + break; + case NPC_BRIDGE_WORKER_TRENT: + worker->AI()->Talk(TALK_TRENT_SCARED, me); + break; + case NPC_BRIDGE_WORKER_DMITRI: + worker->AI()->Talk(TALK_DMITRI_SCARED, me); + break; + case NPC_BRIDGE_WORKER_JESS: + worker->AI()->Talk(TALK_JESS_SCARED, me); + break; + case NPC_BRIDGE_WORKER_DANIEL: + worker->AI()->Talk(TALK_DANIEL_SCARED, me); + break; + default: + break; } - _events.Repeat(Seconds(22), Seconds(30)); - break; - case EVENT_WORKERS_SAY: - if (Creature* trent = ObjectAccessor::GetCreature(*me, _trentGUID)) - trent->AI()->Talk(SAY_WORKERS_HO); - if (Creature* dimitri = ObjectAccessor::GetCreature(*me, _dimitriGUID)) - dimitri->AI()->Talk(SAY_WORKERS_HO); - if (Creature* jess = ObjectAccessor::GetCreature(*me, _jessGUID)) - jess->AI()->Talk(SAY_WORKERS_HO); - if (Creature* daniel = ObjectAccessor::GetCreature(*me, _danielGUID)) - daniel->AI()->Talk(SAY_WORKERS_HO); - if (Creature* matthew = ObjectAccessor::GetCreature(*me, _matthewGUID)) - matthew->AI()->Talk(SAY_WORKERS_HO); - break; - case EVENT_ALEX_SAY_PUSH: - Talk(SAY_ALEX_PUSH); - _events.ScheduleEvent(EVENT_MATTHEW_SAY, Seconds(4)); - break; - case EVENT_MATTHEW_SAY: - if (Creature* matthew = ObjectAccessor::GetCreature(*me, _matthewGUID)) - matthew->AI()->Talk(SAY_MATTHEW_IM_PUSHING); - break; - default: - break; } + break; + } + case EVENT_OSLOW_GET_UP: + { + if (Creature* oslow = ObjectAccessor::GetCreature(*me, _oslowGUID)) + oslow->SetStandState(UNIT_STAND_STATE_STAND); + + _events.ScheduleEvent(EVENT_OSLOW_STUN, 2s); + break; + } + case EVENT_OSLOW_STUN: + { + if (Creature* oslow = ObjectAccessor::GetCreature(*me, _oslowGUID)) + oslow->SetEmoteState(EMOTE_STATE_STUN); + break; + } + case EVENT_DONE: + { + if (Creature* alex = ObjectAccessor::GetCreature(*me, _alexGUID)) + alex->DespawnOrUnsummon(); + if (Creature* matthew = ObjectAccessor::GetCreature(*me, _matthewGUID)) + matthew->DespawnOrUnsummon(); + if (Creature* trent = ObjectAccessor::GetCreature(*me, _trentGUID)) + trent->DespawnOrUnsummon(); + if (Creature* dmitri = ObjectAccessor::GetCreature(*me, _dmitriGUID)) + dmitri->DespawnOrUnsummon(); + if (Creature* jess = ObjectAccessor::GetCreature(*me, _jessGUID)) + jess->DespawnOrUnsummon(); + if (Creature* daniel = ObjectAccessor::GetCreature(*me, _danielGUID)) + daniel->DespawnOrUnsummon(); + if (Creature* oslow = ObjectAccessor::GetCreature(*me, _oslowGUID)) + oslow->DespawnOrUnsummon(); + + _events.Reset(); + me->DespawnOrUnsummon(); + break; } + default: + break; } } + } - private: - EventMap _events; - ObjectGuid _oslowGUID; - ObjectGuid _trentGUID; - ObjectGuid _dimitriGUID; - ObjectGuid _jessGUID; - ObjectGuid _danielGUID; - ObjectGuid _matthewGUID; - }; + void DoAction(int32 const param) override + { + switch (param) + { + case ACTION_OSLOW_GET_UP: + _events.ScheduleEvent(EVENT_OSLOW_GET_UP, 500ms); + + // in case player who started event disconnects + _events.ScheduleEvent(EVENT_DONE, 120s); + break; + case ACTION_COWER: + _events.ScheduleEvent(EVENT_BRIDGE_WORKERS_COWER, 3s); + break; + case ACTION_DONE: + _events.ScheduleEvent(EVENT_DONE, 0s); + break; + default: + break; + } + } - CreatureAI* GetAI(Creature* creature) const override +private: + EventMap _events; + ObjectGuid _oslowGUID; + ObjectGuid _alexGUID; + ObjectGuid _trentGUID; + ObjectGuid _dmitriGUID; + ObjectGuid _jessGUID; + ObjectGuid _danielGUID; + ObjectGuid _matthewGUID; +}; + +constexpr Position EttinNearBoulderPosition = { -9272.053f, -2291.7463f, 68.54081f }; + +// 43197 - Subdued Canyon Ettin +struct npc_redridge_subdued_canyon_ettin : public CreatureAI +{ + npc_redridge_subdued_canyon_ettin(Creature* creature) : CreatureAI(creature) { } + + void Reset() override + { + me->SetReactState(REACT_PASSIVE); + _events.Reset(); + } + + void OnDespawn() override + { + Unit* owner = me->GetOwner(); + if (!owner) + return; + + owner->RemoveAurasDueToSpell(SPELL_CANYON_ETTIN_SPAWN_SPELL); + } + + void SpellHit(WorldObject* caster, SpellInfo const* spell) override + { + if (!caster || !caster->IsPlayer()) + return; + + switch (spell->Id) + { + case SPELL_BOULDER_AURA: + me->GetMotionMaster()->MovePoint(POINT_NEAR_BOULDER, EttinNearBoulderPosition, true, {}, {}, MovementWalkRunSpeedSelectionMode::ForceWalk); + break; + case SPELL_DESPAWN_KILL_CREDIT: + if (Player* player = ObjectAccessor::GetPlayer(*me, caster->GetGUID())) + player->RewardPlayerAndGroupAtEvent(NPC_FOREMAN_OSLOW, player); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_ETTIN_LINE_1: + Talk(TALK_NOT_SO_HEAVY); + _events.ScheduleEvent(EVENT_ETTIN_LINE_2, 3s); + break; + case EVENT_ETTIN_LINE_2: + Talk(TALK_WHERE_THROW); + + if (Creature* boulder = ObjectAccessor::GetCreature(*me, _boulderGUID)) + boulder->AI()->DoAction(ACTION_COWER); + + _events.ScheduleEvent(EVENT_MOVE_TO_WATER, 9s); + break; + case EVENT_MOVE_TO_WATER: + me->GetMotionMaster()->MovePath(PATH_ETTIN_TO_WATER, false); + Talk(TALK_THROW_IN_WATER); + break; + case EVENT_THROW_BOULDER: + me->CastSpell(nullptr, SPELL_EJECT_PASSENGER_1); + _events.ScheduleEvent(EVENT_PATH_AWAY, 6s); + break; + case EVENT_PATH_AWAY: + Talk(TALK_BYE); + + if (Creature* boulder = ObjectAccessor::GetCreature(*me, _boulderGUID)) + boulder->AI()->DoAction(ACTION_DONE); + + me->GetMotionMaster()->MovePath(PATH_ETTIN_UP_HILL, false); + break; + default: + break; + } + } + } + + void MovementInform(uint32 movementType, uint32 pointId) override + { + if (movementType != POINT_MOTION_TYPE) + return; + + if (pointId == POINT_NEAR_BOULDER) + { + if (Creature const* boulder = me->FindNearestCreature(NPC_HUGE_BOULDER, 5.0f, true)) + { + _boulderGUID = boulder->GetGUID(); + me->CastSpell(nullptr, SPELL_LIFT_HUGE_BOULDER, false); + + if (Creature const* daniel = me->FindNearestCreature(NPC_BRIDGE_WORKER_DANIEL, 20.0f, true)) + me->SetFacingToObject(daniel); + + _events.ScheduleEvent(EVENT_ETTIN_LINE_1, 2500ms); + boulder->AI()->DoAction(ACTION_OSLOW_GET_UP); + } + } + } + + void WaypointPathEnded(uint32 /*nodeId*/, uint32 pathId) override { - return new npc_bridge_worker_alexAI(creature); + if (pathId == PATH_ETTIN_TO_WATER) + { + _events.ScheduleEvent(EVENT_THROW_BOULDER, 0s); + } + else if (pathId == PATH_ETTIN_UP_HILL) + { + me->CastSpell(nullptr, SPELL_CANYON_ETTIN_DESPAWN); + me->CastSpell(nullptr, SPELL_DESPAWN_ETTIN); + } + } + +private: + EventMap _events; + ObjectGuid _boulderGUID; +}; + +// 80704 - Control Ettin +class spell_redridge_control_ettin : public SpellScript +{ + SpellCastResult CheckCast() + { + // check for a spawn spell with a valid caster (subdued ettin) + Aura const* spawnAura = GetCaster()->GetAura(SPELL_CANYON_ETTIN_SPAWN_SPELL, [](Aura const* aura) + { + return aura->GetCaster() != nullptr; + }); + + if (spawnAura != nullptr) + { + // fail if the subdued ettin is no longer alive + if (!spawnAura->GetCaster()->IsAlive()) + return SPELL_FAILED_BAD_IMPLICIT_TARGETS; + } + else + { + // no spawn spell, so require nearby canyon ettin + if (!GetCaster()->FindNearestCreature(NPC_CANYON_ETTIN, GetSpellInfo()->GetMaxRange(), true)) + return SPELL_FAILED_OUT_OF_RANGE; + } + + return SPELL_CAST_OK; + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) const + { + if (Unit const* target = GetHitUnit()) + { + // conditions ensure this ettin is owned by player + if (target->GetEntry() == NPC_SUBDUED_CANYON_ETTIN) + { + GetCaster()->CastSpell(nullptr, SPELL_BOULDER_AURA, false); + GetCaster()->CastSpell(nullptr, SPELL_DESPAWN_KILL_CREDIT, false); + } + else + GetCaster()->CastSpell(nullptr, SPELL_CONTROL_ETTIN_2, false); + } + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_redridge_control_ettin::CheckCast); + OnEffectHitTarget += SpellEffectFn(spell_redridge_control_ettin::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_DUMMY); + } +}; + +// 80702 - Control Ettin +class spell_redridge_control_ettin_2 : public SpellScript +{ + void HandleScriptEffect(SpellEffIndex /*effIndex*/) const + { + if (Creature* target = GetHitCreature()) + target->DespawnOrUnsummon(); + + // subdued ettin minion casts 82558 to player + std::list<TempSummon*> minionList; + GetCaster()->GetAllMinionsByEntry(minionList, NPC_SUBDUED_CANYON_ETTIN); + if (TempSummon* ettin = minionList.front()) + ettin->CastSpell(GetCaster(), SPELL_CANYON_ETTIN_SPAWN_SPELL, false); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_redridge_control_ettin_2::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); } }; @@ -270,8 +700,6 @@ enum RedridgeCitizen SAY_IN_TOWNHALL = 0, // Used by npc's in Lakeshire Townhall SAY_LEAVE_TOWNHALL = 1, // Used by npc's in Lakeshire Townhall - - SPELL_APPLY_QUEST_INVIS_ZONE_19 = 82099 // Used by npc's in Lakeshire Townhall }; const Emote EmoteID[6] = @@ -370,6 +798,9 @@ void AddSC_redridge_mountains() { new npc_big_earl(); new npc_dumpy_and_keeshan(); - new npc_bridge_worker_alex(); + RegisterCreatureAI(npc_redridge_huge_boulder); + RegisterCreatureAI(npc_redridge_subdued_canyon_ettin); + RegisterSpellScript(spell_redridge_control_ettin); + RegisterSpellScript(spell_redridge_control_ettin_2); new npc_redridge_citizen(); } |