diff --git a/data/sql/updates/pending_db_world/rev_1727679643827269473.sql b/data/sql/updates/pending_db_world/rev_1727679643827269473.sql new file mode 100644 index 000000000..8e3d2ab45 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1727679643827269473.sql @@ -0,0 +1,149 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=39953 AND `ScriptName`='spell_gen_adals_song_of_battle'; +-- A'dal +UPDATE `smart_scripts` SET `link` = 5 WHERE (`entryorguid` = 18481) AND (`source_type` = 0) AND (`id` = 4); +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 18481) AND (`source_type` = 0) AND (`id` = 5); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(18481, 0, 5, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 237, 39953, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'A Dal - On Quest Reward 11007 - Run World State Script Event 39953'); + +-- Magtheridon's Head +UPDATE `gameobject_template` SET `ScriptName` = 'go_magtheridons_head' WHERE (`entry` = 184640); + +-- Trollbane +UPDATE `smart_scripts` SET `link` = 4 WHERE (`entryorguid` = 16819) AND (`source_type` = 0) AND (`id` = 2); +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 16819) AND (`source_type` = 0) AND (`id` = 4); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16819, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 50, 184640, 7200, 0, 0, 0, 0, 8, 0, 0, 0, 0, -732.28, 2670.99, 94.5875, -0.541051, 'Force Commander Danath Trollbane - On Quest \'The Fall of Magtheridon\' Finished - Summon Gameobject \'Magtheridon\'s Head\' Alliance'); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 5) AND (`SourceEntry` = 16819); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 5, 16819, 3, 0, 103, 0, 39911, 0, 0, 1, 0, 0, '', 'if Gameobject \'Magtheridon\'s Head\' Alliance is not already spawned'); + +-- Nazgrel +UPDATE `smart_scripts` SET `link` = 3 WHERE (`entryorguid` = 3230) AND (`source_type` = 0) AND (`id` = 1); +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 3230) AND (`source_type` = 0) AND (`id` = 3); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(3230, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 50, 184640, 7200, 0, 0, 0, 0, 8, 0, 0, 0, 0, 143.417, 2673.36, 85.3014, 3.01941, 'Nazgrel - On Quest \'The Fall of Magtheridon\' Finished - Summon Gameobject \'Magtheridon\'s Head\' Horde'); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 4) AND (`SourceEntry` = 3230); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 4, 3230, 3, 0, 103, 0, 39913, 0, 0, 1, 0, 0, '', 'if Gameobject \'Magtheridon\'s Head\' Horde is not already spawned'); + +-- Zeppelin transports +SET @RADIUS:=24; -- radius unknown +-- arrival events, play zeppelin horn +DELETE FROM `event_scripts` WHERE `command` = 16 and `id` IN (15312, 15314, 15318, 15320, 15322, 15324, 15430, 15431, 19126, 19127, 19137, 19139, 21868, 21870); +INSERT INTO `event_scripts` (`id`, `delay`, `command`, `datalong`, `datalong2`, `dataint`, `x`, `y`, `z`, `o`) VALUES +(15312, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(15314, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(15318, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(15320, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(15322, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(15324, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(15430, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(15431, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(19126, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(19127, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(19137, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(19139, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(21868, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0), +(21870, 0, 16, 11804, 4, @RADIUS, 0.0, 0.0, 0.0, 0.0); + +-- The Iron Eagle - Grom'Gol to Orgrimmar +UPDATE `gameobject_template` SET `ScriptName` = 'go_transport_the_iron_eagle' WHERE (`entry` = 175080); + +-- Snurk Bucksquick Master Orgrimmar The Iron Eagle +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 3841; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(3841, 0, 0, 'Where is the zeppelin now?', 22086, 1, 1, 8764, 0, 0, 0, '', 0, 0); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 3841) AND (`SourceEntry` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 3841, 0, 0, 0, 103, 0, 175080, 15322, 0, 1, 0, 0, '', 'The zeppelin should not have just arrived at Orgrimmar'); + +-- Zez'raz Master Grom'gol The Iron Eagle +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 2441; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(2441, 0, 0, 'Where is the zeppelin now?', 22086, 1, 1, 8764, 0, 0, 0, '', 0, 0); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 2441); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 2441, 0, 0, 0, 103, 0, 175080, 15324, 0, 1, 0, 0, '', 'The zeppelin should not have just arrived at Grom\'gol'); + +-- Krixx Engineer Orgrimmar The Iron Eagle, The Thundercaller +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 8764); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 8764, 11167, 0, 0, 103, 0, 175080, 15324, 0, 0, 0, 0, '', 'The zeppelin should have just arrived at Grom\'gol'), +(14, 8764, 11169, 0, 1, 103, 0, 175080, 15322, 0, 0, 0, 0, '', 'The zeppelin should have just arrived at Orgrimmar'), +(14, 8764, 11170, 0, 2, 103, 0, 175080, 15323, 0, 0, 0, 0, '', 'The zeppelin should have just departed from Orgrimmar'), +(14, 8764, 11172, 0, 3, 103, 0, 175080, 15325, 0, 0, 0, 0, '', 'The zeppelin should have just departed from Grom\'gol'), +(14, 8764, 11163, 0, 4, 103, 0, 175080, 0, 0, 0, 0, 0, '', 'I\'m not sure where the zeppelin is right now, actually...'); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 8765); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 8765, 11165, 0, 0, 103, 0, 164871, 15318, 0, 0, 0, 0, '', 'The zeppelin should have just arrived at Orgrimmar'), +(14, 8765, 11173, 0, 1, 103, 0, 164871, 15320, 0, 0, 0, 0, '', 'The zeppelin should have just arrived at Undercity'), +(14, 8765, 11174, 0, 2, 103, 0, 164871, 15319, 0, 0, 0, 0, '', 'The zeppelin should have just departed from Orgrimmar'), +(14, 8765, 11175, 0, 3, 103, 0, 164871, 15321, 0, 0, 0, 0, '', 'The zeppelin should have just departed from Undercity'), +(14, 8765, 11163, 0, 4, 103, 0, 164871, 0, 0, 0, 0, 0, '', 'I\'m not sure where the zeppelin is right now, actually...'); + +-- The Thundercaller - Undercity to Orgrimmar +UPDATE `gameobject_template` SET `ScriptName` = 'go_transport_the_thundercaller' WHERE (`entry` = 164871); + +-- Frezza Master Orgrimmar The Thundercaller +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 1969; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(1969, 0, 0, 'Where is the zeppelin now?', 22086, 1, 1, 8765, 0, 0, 0, '', 0, 0); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 1969) AND (`SourceEntry` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 1969, 0, 0, 0, 103, 0, 164871, 15318, 0, 1, 0, 0, '', 'The zeppelin should not have just arrived at Orgrimmar'); + +-- Zapetta Master Undercity The Thundercaller +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 1971; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(1971, 0, 0, 'Where is the zeppelin now?', 22086, 1, 1, 8765, 0, 0, 0, '', 0, 0); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 1971) AND (`SourceEntry` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 1971, 0, 0, 0, 103, 0, 164871, 15320, 0, 1, 0, 0, '', 'The zeppelin should not have just arrived at Undercity'); +DELETE FROM `creature_text` WHERE (`CreatureID` = 9566) AND (`GroupID` = 1); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(9566, 1, 0, 'There goes the zeppelin to Orgrimmar. I hope there\'s no explosions this time.', 12, 0, 100, 0, 0, 0, 22080, 0, 'Zapetta - Departure'); +UPDATE `creature_text` SET `comment` = 'Zapetta - Arrival' WHERE (`CreatureID` = 9566) AND (`GroupID` = 0); + +-- The Purple Princess - Grom'Gol to Undercity +UPDATE `gameobject_template` SET `ScriptName` = 'go_transport_the_purple_princess' WHERE (`entry` = 176495); + +-- Hin Denburg Master Undercity The Purple Princess +DELETE FROM `gossip_menu` WHERE `MenuID` = 8766 AND `TextID` IN (11179, 11182); +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(8766, 11179), +(8766, 11182); +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 2101; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(2101, 0, 0, 'Where is the zeppelin now?', 22086, 1, 1, 8766, 0, 0, 0, '', 0, 0); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 2101) AND (`SourceEntry` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 2101, 0, 0, 0, 103, 0, 176495, 15312, 0, 1, 0, 0, '', 'The zeppelin should not have just arrived at Undercity'); + +-- Squibby Overspeck Master Grom'gol The Purple Princess +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 3842; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(3842, 0, 0, 'Where is the zeppelin now?', 22086, 1, 1, 8766, 0, 0, 0, '', 0, 0); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 3842) AND (`SourceEntry` = 0) AND (`SourceId` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 3842, 0, 0, 0, 103, 0, 176495, 15314, 0, 1, 0, 0, '', 'The zeppelin should not have just arrived at Grom\'gol'); + +-- Kraxx Engineer Undercity The Thundercaller, The Purple Princess +DELETE FROM `gossip_menu_option` WHERE `MenuID` = 8786; +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(8786, 0, 0, 'Where is the zeppelin to Orgrimmar right now?', 22185, 1, 1, 8765, 0, 0, 0, '', 0, 0), +(8786, 1, 0, 'Where is the zeppelin to Grom\'gol right now?', 22199, 1, 1, 8766, 0, 0, 0, '', 0, 0); +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 14) AND (`SourceGroup` = 8766); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(14, 8766, 11179, 0, 0, 103, 0, 176495, 15312, 0, 0, 0, 0, '', 'The zeppelin should have just arrived at Undercity'), +(14, 8766, 11180, 0, 1, 103, 0, 176495, 15314, 0, 0, 0, 0, '', 'The zeppelin should have just arrived at Grom\'gol'), +(14, 8766, 11182, 0, 2, 103, 0, 176495, 15313, 0, 0, 0, 0, '', 'The zeppelin should have just departed from Undercity'), +(14, 8766, 11181, 0, 3, 103, 0, 176495, 15315, 0, 0, 0, 0, '', 'The zeppelin should have just departed from Grom\'gol'), +(14, 8766, 11163, 0, 4, 103, 0, 176495, 0, 0, 0, 0, 0, '', 'I\'m not sure where the zeppelin is right now, actually...'); + +-- I\'m not sure where the zeppelin is right now, actually... +DELETE FROM `gossip_menu` WHERE (`MenuID` IN (8764, 8765, 8766)) and `TextID` = 11163; +INSERT INTO `gossip_menu` (`MenuID`, `TextID`) VALUES +(8764, 11163), +(8765, 11163), +(8766, 11163); diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 4605b9fba..f2c8412c3 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -35,6 +35,7 @@ #include "SmartAI.h" #include "SpellMgr.h" #include "Vehicle.h" +#include "WorldState.h" /// @todo: this import is not necessary for compilation and marked as unused by the IDE // however, for some reasons removing it would cause a damn linking issue @@ -3269,6 +3270,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u break; } + case SMART_ACTION_WORLD_SCRIPT: + { + sWorldState->HandleExternalEvent(static_cast(e.action.worldStateScript.eventId), e.action.worldStateScript.param); + break; + } default: LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Unhandled Action type {}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); break; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 69e8e5d74..b309f4696 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -777,6 +777,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e) case SMART_ACTION_MOVEMENT_STOP: return NO_PARAMS; case SMART_ACTION_MOVEMENT_PAUSE: return sizeof(SmartAction::move); case SMART_ACTION_MOVEMENT_RESUME: return sizeof(SmartAction::move); + case SMART_ACTION_WORLD_SCRIPT: return sizeof(SmartAction::worldStateScript); default: LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an action with no unused params specified in SmartAIMgr::CheckUnusedActionParams(), please report this.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); @@ -1960,6 +1961,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) case SMART_ACTION_MOVEMENT_STOP: case SMART_ACTION_MOVEMENT_PAUSE: case SMART_ACTION_MOVEMENT_RESUME: + case SMART_ACTION_WORLD_SCRIPT: break; default: LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type({}), event_type({}), Entry {} SourceType {} Event {}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id); diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 73212b424..b713054d6 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -727,8 +727,9 @@ enum SMART_ACTION SMART_ACTION_MOVEMENT_STOP = 234, // SMART_ACTION_MOVEMENT_PAUSE = 235, // timer SMART_ACTION_MOVEMENT_RESUME = 236, // timerOverride + SMART_ACTION_WORLD_SCRIPT = 237, // eventId, param - SMART_ACTION_AC_END = 237, // placeholder + SMART_ACTION_AC_END = 238, // placeholder }; enum class SmartActionSummonCreatureFlags @@ -1472,6 +1473,12 @@ struct SmartAction { uint32 timer; } move; + + struct + { + uint32 eventId; + uint32 param; + } worldStateScript; //! Note for any new future actions //! All parameters must have type uint32 diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 805fcd06d..91e2c1a96 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -28,6 +28,7 @@ #include "Spell.h" #include "SpellAuras.h" #include "SpellMgr.h" +#include "WorldState.h" // Checks if object meets the condition // Can have CONDITION_SOURCE_TYPE_NONE && !mReferenceId if called from a special event (ie: eventAI) @@ -570,6 +571,11 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) condMeets = unit->IsCharmed(); break; } + case CONDITION_WORLD_SCRIPT: + { + condMeets = sWorldState->IsConditionFulfilled(static_cast(ConditionValue1), static_cast(ConditionValue2)); + break; + } default: condMeets = false; break; @@ -770,6 +776,9 @@ uint32 Condition::GetSearcherTypeMaskForCondition() case CONDITION_CHARMED: mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER; break; + case CONDITION_WORLD_SCRIPT: + mask |= GRID_MAP_TYPE_MASK_ALL; + break; default: ASSERT(false && "Condition::GetSearcherTypeMaskForCondition - missing condition handling!"); break; diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index 49620aa3f..1540fd903 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -86,7 +86,9 @@ enum ConditionTypes CONDITION_AC_START = 100, CONDITION_QUEST_SATISFY_EXCLUSIVE = 101, // quest_id 0 0 true if satisfied exclusive group CONDITION_HAS_AURA_TYPE = 102, // aura_type 0 0 true if has aura type - CONDITION_AC_END = 103 // placeholder + CONDITION_WORLD_SCRIPT = 103, // conditionId state 0 true if WorldState::IsConditionFulfilled returns true + + CONDITION_AC_END = 104 // placeholder }; /*! Documentation on implementing a new ConditionSourceType: diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index d53454011..d757f1a13 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -83,6 +83,7 @@ #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "WorldState.h" #include /// @todo: this import is not necessary for compilation and marked as unused by the IDE @@ -1722,6 +1723,7 @@ void Player::RemoveFromWorld() m_session->DoLootRelease(lguid); sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); + sWorldState->HandlePlayerLeaveZone(this, static_cast(m_zoneUpdateId)); } // Remove items from world before self - player must be found in Item::RemoveFromObjectUpdate diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 5dcf409fa..f03de45c7 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -38,6 +38,7 @@ #include "Vehicle.h" #include "Weather.h" #include "WeatherMgr.h" +#include "WorldState.h" #include "WorldStatePackets.h" /// @todo: this import is not necessary for compilation and marked as unused by the IDE @@ -1215,6 +1216,8 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) { sOutdoorPvPMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); sOutdoorPvPMgr->HandlePlayerEnterZone(this, newZone); + sWorldState->HandlePlayerLeaveZone(this, static_cast(m_zoneUpdateId)); + sWorldState->HandlePlayerEnterZone(this, static_cast(newZone)); sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); sBattlefieldMgr->HandlePlayerEnterZone(this, newZone); SendInitWorldStates(newZone, diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 16be17fcf..8c25ade40 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -180,6 +180,7 @@ enum eScriptFlags // Playsound flags SF_PLAYSOUND_TARGET_PLAYER = 0x1, SF_PLAYSOUND_DISTANCE_SOUND = 0x2, + SF_PLAYSOUND_DISTANCE_RADIUS = 0x4, // Orientation flags SF_ORIENTATION_FACE_TARGET = 0x1, @@ -307,6 +308,7 @@ struct ScriptInfo { uint32 SoundID; // datalong uint32 Flags; // datalong2 + int32 Radius; // dataint } Playsound; struct // SCRIPT_COMMAND_CREATE_ITEM (17) diff --git a/src/server/game/Scripting/MapScripts.cpp b/src/server/game/Scripting/MapScripts.cpp index ebf2d3daa..9cbc20f21 100644 --- a/src/server/game/Scripting/MapScripts.cpp +++ b/src/server/game/Scripting/MapScripts.cpp @@ -730,8 +730,10 @@ void Map::ScriptsProcess() break; } - // Playsound.Flags bitmask: 0/2=without/with distance dependent - if (step.script->Playsound.Flags & SF_PLAYSOUND_DISTANCE_SOUND) + // Playsound.Flags bitmask: 0/2/4=without/with distance dependent/radius + if (step.script->Playsound.Flags & SF_PLAYSOUND_DISTANCE_RADIUS) + object->PlayRadiusSound(step.script->Playsound.SoundID, step.script->Playsound.Radius); + else if (step.script->Playsound.Flags & SF_PLAYSOUND_DISTANCE_SOUND) object->PlayDistanceSound(step.script->Playsound.SoundID, player); else object->PlayDirectSound(step.script->Playsound.SoundID, player); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 14182c9ec..769985848 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -49,6 +49,7 @@ #include "World.h" #include "WorldPacket.h" #include "WorldSocket.h" +#include "WorldState.h" #include #include "BanMgr.h" @@ -617,6 +618,7 @@ void WorldSession::LogoutPlayer(bool save) _player->RepopAtGraveyard(); sOutdoorPvPMgr->HandlePlayerLeaveZone(_player, _player->GetZoneId()); + sWorldState->HandlePlayerLeaveZone(_player, static_cast(_player->GetZoneId())); // pussywizard: remove from battleground queues on logout for (int i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i) diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 6863984ce..36015f5cf 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -1389,18 +1389,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(13); // 50000yd }); - // A'dal's Song of Battle - ApplySpellFix({ 39953 }, [](SpellInfo* spellInfo) - { - spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_SRC_CASTER); - spellInfo->Effects[EFFECT_1].TargetA = SpellImplicitTargetInfo(TARGET_SRC_CASTER); - spellInfo->Effects[EFFECT_2].TargetA = SpellImplicitTargetInfo(TARGET_SRC_CASTER); - spellInfo->Effects[EFFECT_0].TargetB = SpellImplicitTargetInfo(TARGET_UNIT_SRC_AREA_ALLY); - spellInfo->Effects[EFFECT_1].TargetB = SpellImplicitTargetInfo(TARGET_UNIT_SRC_AREA_ALLY); - spellInfo->Effects[EFFECT_2].TargetB = SpellImplicitTargetInfo(TARGET_UNIT_SRC_AREA_ALLY); - spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(367); // 2 Hours - }); - ApplySpellFix({ 57607, // WintergraspCatapult - Spell Plague Barrel - EffectRadiusIndex 57619, // WintergraspDemolisher - Spell Hourl Boulder - EffectRadiusIndex diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 01295f569..fb910444e 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -92,6 +92,7 @@ #include "WhoListCacheMgr.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "WorldState.h" #include #include @@ -2411,6 +2412,11 @@ void World::Update(uint32 diff) sOutdoorPvPMgr->Update(diff); } + { + METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update worldstate")); + sWorldState->Update(diff); + } + { METRIC_TIMER("world_update_time", METRIC_TAG("type", "Update battlefields")); sBattlefieldMgr->Update(diff); diff --git a/src/server/game/World/WorldState.cpp b/src/server/game/World/WorldState.cpp new file mode 100644 index 000000000..174e468f4 --- /dev/null +++ b/src/server/game/World/WorldState.cpp @@ -0,0 +1,293 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "MapMgr.h" +#include "Player.h" +#include "SharedDefines.h" +#include "WorldState.h" + +WorldState* WorldState::instance() +{ + static WorldState instance; + return &instance; +} + +WorldState::WorldState() : _isMagtheridonHeadSpawnedHorde(false), _isMagtheridonHeadSpawnedAlliance(false) +{ + _transportStates[WORLD_STATE_CONDITION_THE_IRON_EAGLE] = WORLD_STATE_CONDITION_STATE_NONE; + _transportStates[WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS] = WORLD_STATE_CONDITION_STATE_NONE; + _transportStates[WORLD_STATE_CONDITION_THE_THUNDERCALLER] = WORLD_STATE_CONDITION_STATE_NONE; +} + +WorldState::~WorldState() +{ +} + +bool WorldState::IsConditionFulfilled(WorldStateCondition conditionId, WorldStateConditionState state) const +{ + switch (conditionId) + { + case WORLD_STATE_CONDITION_TROLLBANES_COMMAND: + return _isMagtheridonHeadSpawnedAlliance; + case WORLD_STATE_CONDITION_NAZGRELS_FAVOR: + return _isMagtheridonHeadSpawnedHorde; + case WORLD_STATE_CONDITION_THE_IRON_EAGLE: + case WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS: + case WORLD_STATE_CONDITION_THE_THUNDERCALLER: + return _transportStates.at(conditionId) == state; + default: + LOG_ERROR("scripts", "WorldState::IsConditionFulfilled: Unhandled WorldStateCondition {}", conditionId); + return false; + } +} + +void WorldState::HandleConditionStateChange(WorldStateCondition conditionId, WorldStateConditionState state) +{ + _transportStates[conditionId] = state; +} + +void WorldState::HandleExternalEvent(WorldStateEvent eventId, uint32 param) +{ + std::lock_guard guard(_mutex); + switch (eventId) + { + case WORLD_STATE_CUSTOM_EVENT_ON_ADALS_SONG_OF_BATTLE: + if (!_adalSongOfBattleTimer) + { + _adalSongOfBattleTimer = 120 * MINUTE * IN_MILLISECONDS; + BuffAdalsSongOfBattle(); + } + break; + case WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_SPAWN: + if (param == TEAM_ALLIANCE) + { + _isMagtheridonHeadSpawnedAlliance = true; + BuffMagtheridonTeam(TEAM_ALLIANCE); + } + else + { + _isMagtheridonHeadSpawnedHorde = true; + BuffMagtheridonTeam(TEAM_HORDE); + } + break; + case WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_DESPAWN: + if (param == TEAM_ALLIANCE) + { + _isMagtheridonHeadSpawnedAlliance = false; + DispelMagtheridonTeam(TEAM_ALLIANCE); + } + else + { + _isMagtheridonHeadSpawnedHorde = false; + DispelMagtheridonTeam(TEAM_HORDE); + } + break; + default: + break; + } +} + +void WorldState::Update(uint32 diff) +{ + if (_adalSongOfBattleTimer) + { + if (_adalSongOfBattleTimer <= diff) + { + _adalSongOfBattleTimer = 0; + DispelAdalsSongOfBattle(); + } + else + { + _adalSongOfBattleTimer -= diff; + } + } +} + +void WorldState::HandlePlayerEnterZone(Player* player, WorldStateZoneId zoneId) +{ + std::lock_guard guard(_mutex); + switch (zoneId) + { + case ZONEID_SHATTRATH: + case ZONEID_BOTANICA: + case ZONEID_MECHANAR: + case ZONEID_ARCATRAZ: + if (_adalSongOfBattleTimer) + player->CastSpell(player, SPELL_ADAL_SONG_OF_BATTLE, true); + break; + case ZONEID_HELLFIRE_PENINSULA: + case ZONEID_HELLFIRE_RAMPARTS: + case ZONEID_HELLFIRE_CITADEL: + case ZONEID_BLOOD_FURNACE: + case ZONEID_SHATTERED_HALLS: + case ZONEID_MAGTHERIDON_LAIR: + if (_isMagtheridonHeadSpawnedAlliance && player->GetTeamId() == TEAM_ALLIANCE) + player->CastSpell(player, SPELL_TROLLBANES_COMMAND, true); + else if (_isMagtheridonHeadSpawnedHorde && player->GetTeamId() == TEAM_HORDE) + player->CastSpell(player, SPELL_NAZGRELS_FAVOR, true); + break; + default: + break; + } +}; +void WorldState::HandlePlayerLeaveZone(Player* player, WorldStateZoneId zoneId) +{ + std::lock_guard guard(_mutex); + switch (zoneId) + { + case ZONEID_SHATTRATH: + case ZONEID_BOTANICA: + case ZONEID_MECHANAR: + case ZONEID_ARCATRAZ: + if (!_adalSongOfBattleTimer) + player->RemoveAurasDueToSpell(SPELL_ADAL_SONG_OF_BATTLE); + break; + case ZONEID_HELLFIRE_PENINSULA: + case ZONEID_HELLFIRE_RAMPARTS: + case ZONEID_HELLFIRE_CITADEL: + case ZONEID_BLOOD_FURNACE: + case ZONEID_SHATTERED_HALLS: + case ZONEID_MAGTHERIDON_LAIR: + if (player->GetTeamId() == TEAM_ALLIANCE) + player->RemoveAurasDueToSpell(SPELL_TROLLBANES_COMMAND); + else if (player->GetTeamId() == TEAM_HORDE) + player->RemoveAurasDueToSpell(SPELL_NAZGRELS_FAVOR); + break; + default: + break; + } +}; + +void WorldState::BuffMagtheridonTeam(TeamId team) +{ + sMapMgr->DoForAllMaps([&](Map* map) -> void + { + switch (map->GetId()) + { + case 530: // Outland + map->DoForAllPlayers([&](Player* player) + { + if (player->GetZoneId() == ZONEID_HELLFIRE_PENINSULA && player->GetTeamId() == TEAM_ALLIANCE && team == TEAM_ALLIANCE) + player->CastSpell(player, SPELL_TROLLBANES_COMMAND, true); + else if (player->GetZoneId() == ZONEID_HELLFIRE_PENINSULA && player->GetTeamId() == TEAM_HORDE && team == TEAM_HORDE) + player->CastSpell(player, SPELL_NAZGRELS_FAVOR, true); + }); + break; + case 540: // The Shattered Halls + case 542: // The Blood Furnace + case 543: // Ramparts + case 544: // Magtheridon's Lair + map->DoForAllPlayers([&](Player* player) + { + if (player->GetTeamId() == TEAM_ALLIANCE && team == TEAM_ALLIANCE) + player->CastSpell(player, SPELL_TROLLBANES_COMMAND, true); + else if (player->GetTeamId() == TEAM_HORDE && team == TEAM_HORDE) + player->CastSpell(player, SPELL_NAZGRELS_FAVOR, true); + }); + break; + default: + break; + } + }); +} + +void WorldState::DispelMagtheridonTeam(TeamId team) +{ + sMapMgr->DoForAllMaps([&](Map* map) -> void + { + switch (map->GetId()) + { + case 530: // Outland + map->DoForAllPlayers([&](Player* player) + { + if (player->GetZoneId() == ZONEID_HELLFIRE_PENINSULA && player->GetTeamId() == TEAM_ALLIANCE && team == TEAM_ALLIANCE) + player->RemoveAurasDueToSpell(SPELL_TROLLBANES_COMMAND); + else if (player->GetZoneId() == ZONEID_HELLFIRE_PENINSULA && player->GetTeamId() == TEAM_HORDE && team == TEAM_HORDE) + player->RemoveAurasDueToSpell(SPELL_NAZGRELS_FAVOR); + }); + break; + case 540: // The Shattered Halls + case 542: // The Blood Furnace + case 543: // Ramparts + case 544: // Magtheridon's Lair + map->DoForAllPlayers([&](Player* player) + { + if (player->GetTeamId() == TEAM_ALLIANCE && team == TEAM_ALLIANCE) + player->RemoveAurasDueToSpell(SPELL_TROLLBANES_COMMAND); + else if (player->GetTeamId() == TEAM_HORDE && team == TEAM_HORDE) + player->RemoveAurasDueToSpell(SPELL_NAZGRELS_FAVOR); + }); + break; + default: + break; + } + }); +} + +void WorldState::BuffAdalsSongOfBattle() +{ + sMapMgr->DoForAllMaps([&](Map* map) -> void + { + switch (map->GetId()) + { + case 530: // Outland + map->DoForAllPlayers([&](Player* player) + { + if (player->GetZoneId() == ZONEID_SHATTRATH) + player->CastSpell(player, SPELL_ADAL_SONG_OF_BATTLE, true); + }); + break; + case 552: // Arcatraz + case 553: // Botanica + case 554: // Mechanar + map->DoForAllPlayers([&](Player* player) + { + player->CastSpell(player, SPELL_ADAL_SONG_OF_BATTLE, true); + }); + break; + default: + break; + } + }); +} + +void WorldState::DispelAdalsSongOfBattle() +{ + sMapMgr->DoForAllMaps([&](Map* map) -> void + { + switch (map->GetId()) + { + case 530: // Outland + map->DoForAllPlayers([&](Player* player) + { + if (player->GetZoneId() == ZONEID_SHATTRATH) + player->RemoveAurasDueToSpell(SPELL_ADAL_SONG_OF_BATTLE); + }); + break; + case 552: // Arcatraz + case 553: // Botanica + case 554: // Mechanar + map->DoForAllPlayers([&](Player* player) + { + player->RemoveAurasDueToSpell(SPELL_ADAL_SONG_OF_BATTLE); + }); + break; + default: + break; + } + }); +} diff --git a/src/server/game/World/WorldState.h b/src/server/game/World/WorldState.h new file mode 100644 index 000000000..d708bdcaf --- /dev/null +++ b/src/server/game/World/WorldState.h @@ -0,0 +1,95 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef WORLD_STATE_H +#define WORLD_STATE_H + +#include "Player.h" +#include + +enum WorldStateCondition +{ + WORLD_STATE_CONDITION_TROLLBANES_COMMAND = 39911, + WORLD_STATE_CONDITION_NAZGRELS_FAVOR = 39913, + // Zeppelins + WORLD_STATE_CONDITION_THE_THUNDERCALLER = 164871, + WORLD_STATE_CONDITION_THE_IRON_EAGLE = 175080, + WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS = 176495, +}; + +enum WorldStateConditionState +{ + WORLD_STATE_CONDITION_STATE_NONE = 0, +}; + +enum WorldStateEvent +{ + WORLD_STATE_CUSTOM_EVENT_ON_ADALS_SONG_OF_BATTLE = 39953, + WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_SPAWN = 184640, + WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_DESPAWN = 184641, +}; + +enum WorldStateZoneId +{ + ZONEID_SHATTRATH = 3703, + ZONEID_BOTANICA = 3847, + ZONEID_ARCATRAZ = 3848, + ZONEID_MECHANAR = 3849, + + ZONEID_HELLFIRE_PENINSULA = 3483, + ZONEID_HELLFIRE_RAMPARTS = 3562, + ZONEID_HELLFIRE_CITADEL = 3563, + ZONEID_BLOOD_FURNACE = 3713, + ZONEID_SHATTERED_HALLS = 3714, + ZONEID_MAGTHERIDON_LAIR = 3836, +}; + +enum WorldStateSpells +{ + SPELL_ADAL_SONG_OF_BATTLE = 39953, + + SPELL_TROLLBANES_COMMAND = 39911, + SPELL_NAZGRELS_FAVOR = 39913, +}; + +// Intended for implementing server wide scripts, note: all behaviour must be safeguarded towards multithreading +class WorldState +{ + public: + WorldState(); + virtual ~WorldState(); + static WorldState* instance(); + void HandlePlayerEnterZone(Player* player, WorldStateZoneId zoneId); + void HandlePlayerLeaveZone(Player* player, WorldStateZoneId zoneId); + bool IsConditionFulfilled(WorldStateCondition conditionId, WorldStateConditionState state = WORLD_STATE_CONDITION_STATE_NONE) const; + void HandleConditionStateChange(WorldStateCondition conditionId, WorldStateConditionState state); + void HandleExternalEvent(WorldStateEvent eventId, uint32 param); + void Update(uint32 diff); + private: + void BuffAdalsSongOfBattle(); + void DispelAdalsSongOfBattle(); + uint32 _adalSongOfBattleTimer; + void BuffMagtheridonTeam(TeamId team); + void DispelMagtheridonTeam(TeamId team); + bool _isMagtheridonHeadSpawnedHorde; + bool _isMagtheridonHeadSpawnedAlliance; + std::map> _transportStates; // atomic to avoid having to lock + std::mutex _mutex; // all World State operations are threat unsafe +}; + +#define sWorldState WorldState::instance() +#endif diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index 717232742..8d9b2a5be 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -24,6 +24,7 @@ #include "ScriptedGossip.h" #include "SpellScript.h" #include "SpellScriptLoader.h" +#include "WorldState.h" enum q10935Exorcism { @@ -602,6 +603,26 @@ public: } }; +struct go_magtheridons_head : public GameObjectAI +{ + go_magtheridons_head(GameObject* gameObject) : GameObjectAI(gameObject) { } + + void InitializeAI() override + { + me->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); // spawn head on spike + me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + sWorldState->HandleExternalEvent(WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_SPAWN, me->GetPositionX() > 0.f ? TEAM_HORDE : TEAM_ALLIANCE); + } + + void OnStateChanged(uint32 state, Unit* /*unit*/) override + { + if (state == GO_JUST_DEACTIVATED) + { + sWorldState->HandleExternalEvent(WORLD_STATE_CUSTOM_EVENT_ON_MAGTHERIDON_HEAD_DESPAWN, me->GetPositionX() > 0.f ? TEAM_HORDE : TEAM_ALLIANCE); + } + } +}; + void AddSC_hellfire_peninsula() { // Ours @@ -615,4 +636,5 @@ void AddSC_hellfire_peninsula() new go_beacon(); RegisterCreatureAI(npc_magister_aledis); + RegisterGameObjectAI(go_magtheridons_head); } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index d139bccd0..cd916620e 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -554,26 +554,6 @@ class spell_gen_rallying_cry_of_the_dragonslayer : public SpellScript } }; -// 39953 - A'dal's Song of Battle -class spell_gen_adals_song_of_battle : public SpellScript -{ - PrepareSpellScript(spell_gen_adals_song_of_battle); - - void SelectTarget(std::list& targets) - { - targets.clear(); - Map::PlayerList const& pList = GetCaster()->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr) - if (itr->GetSource()->GetZoneId() == 3703 /*Shattrath*/) - targets.push_back(itr->GetSource()); - } - - void Register() override - { - OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gen_adals_song_of_battle::SelectTarget, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY); - } -}; - /* 15366 - Songflower Serenade 22888 - Rallying Cry of the Dragonslayer */ class spell_gen_disabled_above_63 : public AuraScript @@ -5336,7 +5316,6 @@ void AddSC_generic_spell_scripts() RegisterSpellScript(spell_pet_hit_expertise_scalling); RegisterSpellScript(spell_gen_grow_flower_patch); RegisterSpellScript(spell_gen_rallying_cry_of_the_dragonslayer); - RegisterSpellScript(spell_gen_adals_song_of_battle); RegisterSpellScript(spell_gen_disabled_above_63); RegisterSpellScript(spell_gen_black_magic_enchant); RegisterSpellScript(spell_gen_area_aura_select_players); diff --git a/src/server/scripts/World/transport_zeppelin.h b/src/server/scripts/World/transport_zeppelin.h new file mode 100644 index 000000000..232e81200 --- /dev/null +++ b/src/server/scripts/World/transport_zeppelin.h @@ -0,0 +1,68 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +enum ZeppelinEvent +{ + EVENT_UC_FROM_GROMGOL_ARRIVAL = 15312, + EVENT_GROMGOL_FROM_UC_ARRIVAL = 15314, + EVENT_OG_FROM_UC_ARRIVAL = 15318, + EVENT_UC_FROM_OG_ARRIVAL = 15320, + EVENT_OG_FROM_GROMGOL_ARRIVAL = 15322, + EVENT_GROMGOL_FROM_OG_ARRIVAL = 15324, + EVENT_WK_ARRIVAL = 15431, + EVENT_VL_FROM_UC_ARRIVAL = 19126, + EVENT_UC_FROM_VL_ARRIVAL = 19127, + EVENT_OG_FROM_BT_ARRIVAL = 19137, + EVENT_BT_FROM_OG_ARRIVAL = 19139, + EVENT_OG_FROM_TB_ARRIVAL = 21868, + EVENT_TB_FROM_OG_ARRIVAL = 21870, + EVENT_OG_TO_GROMGOL_DEPARTURE = 15323, + EVENT_GROMGOL_TO_OG_DEPARTURE = 15325, + EVENT_OG_TO_UC_DEPARTURE = 15319, + EVENT_UC_TO_OG_DEPARTURE = 15321, + EVENT_UC_TO_GROMGOL_DEPARTURE = 15313, + EVENT_GROMGOL_TO_UC_DEPARTURE = 15315, +}; + +enum ZeppelinMaster +{ + NPC_NEZRAZ = 3149, + NPC_HINDENBURG = 3150, + NPC_FREZZA = 9564, + NPC_ZAPETTA = 9566, + NPC_SNURK_BUCKSQUICK = 12136, + NPC_SQUIBBY_OVERSPECK = 12137, + NPC_HARROWMEISER = 23823, + NPC_GREEB_RAMROCKET = 26537, + NPC_NARGO_SCREWBORE = 26538, + NPC_MEEFI_FARTHROTTLE = 26539, + NPC_DRENK_SPANNERSPARK = 26540, + NPC_ZELLI_HOTNOZZLE = 34765, + NPC_KRENDLE_BIGPOCKETS = 34766, +}; + +const float SEARCH_RANGE_ZEPPELIN_MASTER = 32.0f; + +enum ZeppelinPassenger +{ + // The Thundercaller + NPC_SKY_CAPTAIN_CLOUDKICKER = 25077, + NPC_CHIEF_OFFICER_COPPERNUT = 25070, + // The Purple Princess + NPC_SKY_CAPTAIN_CABLELAMP = 25105, + NPC_WATCHER_UMJIN = 25107, +}; diff --git a/src/server/scripts/World/transport_zeppelins.cpp b/src/server/scripts/World/transport_zeppelins.cpp new file mode 100644 index 000000000..2cb849692 --- /dev/null +++ b/src/server/scripts/World/transport_zeppelins.cpp @@ -0,0 +1,109 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "GameObjectAI.h" +#include "GameObjectScript.h" +#include "WorldState.h" +#include "transport_zeppelin.h" + +// 175080 The Iron Eagle - Grom'gol to Orgrimmar +struct go_transport_the_iron_eagle : GameObjectAI +{ + go_transport_the_iron_eagle(GameObject *object) : GameObjectAI(object) { }; + + void EventInform(uint32 eventId) override + { + sWorldState->HandleConditionStateChange(WORLD_STATE_CONDITION_THE_IRON_EAGLE, static_cast(eventId)); + switch (eventId) + { + case EVENT_GROMGOL_FROM_OG_ARRIVAL: + if (Creature *creature = me->FindNearestCreature(NPC_NEZRAZ, SEARCH_RANGE_ZEPPELIN_MASTER)) + creature->AI()->Talk(0); + break; + case EVENT_OG_FROM_GROMGOL_ARRIVAL: + if (Creature *creature = me->FindNearestCreature(NPC_SNURK_BUCKSQUICK, 150.0f)) + creature->AI()->Talk(0); + break; + default: + return; + } + } +}; + +// 164871 The Thundercaller - Undercity to Orgrimmar +struct go_transport_the_thundercaller : GameObjectAI +{ + go_transport_the_thundercaller(GameObject *object) : GameObjectAI(object) { }; + + void EventInform(uint32 eventId) override + { + sWorldState->HandleConditionStateChange(WORLD_STATE_CONDITION_THE_THUNDERCALLER, static_cast(eventId)); + switch (eventId) + { + case EVENT_OG_FROM_UC_ARRIVAL: + if (Creature *creature = me->FindNearestCreature(NPC_FREZZA, SEARCH_RANGE_ZEPPELIN_MASTER)) + creature->AI()->Talk(0); + break; + case EVENT_UC_FROM_OG_ARRIVAL: + if (Creature *creature = me->FindNearestCreature(NPC_ZAPETTA, SEARCH_RANGE_ZEPPELIN_MASTER)) + creature->AI()->Talk(0); + break; + case EVENT_OG_TO_UC_DEPARTURE: + break; + case EVENT_UC_TO_OG_DEPARTURE: + if (Creature *creature = me->FindNearestCreature(NPC_ZAPETTA, SEARCH_RANGE_ZEPPELIN_MASTER)) + creature->AI()->Talk(1); + break; + default: + return; + } + } +}; + +// 176495 The Purple Princess - Grom'Gol to Undercity +struct go_transport_the_purple_princess : GameObjectAI +{ + go_transport_the_purple_princess(GameObject *object) : GameObjectAI(object) { }; + + void EventInform(uint32 eventId) override + { + sWorldState->HandleConditionStateChange(WORLD_STATE_CONDITION_THE_PURPLE_PRINCESS, static_cast(eventId)); + switch (eventId) + { + case EVENT_GROMGOL_FROM_UC_ARRIVAL: + if (Creature *creature = me->FindNearestCreature(NPC_SQUIBBY_OVERSPECK, SEARCH_RANGE_ZEPPELIN_MASTER)) + creature->AI()->Talk(0); + break; + case EVENT_UC_FROM_GROMGOL_ARRIVAL: + if (Creature *creature = me->FindNearestCreature(NPC_HINDENBURG, SEARCH_RANGE_ZEPPELIN_MASTER)) + creature->AI()->Talk(0); + break; + case EVENT_UC_TO_GROMGOL_DEPARTURE: + case EVENT_GROMGOL_TO_UC_DEPARTURE: + break; + default: + return; + } + } +}; + +void AddSC_transport_zeppelins() +{ + RegisterGameObjectAI(go_transport_the_iron_eagle); + RegisterGameObjectAI(go_transport_the_thundercaller); + RegisterGameObjectAI(go_transport_the_purple_princess); +} diff --git a/src/server/scripts/World/world_script_loader.cpp b/src/server/scripts/World/world_script_loader.cpp index cda3d4ba4..21813fd4c 100644 --- a/src/server/scripts/World/world_script_loader.cpp +++ b/src/server/scripts/World/world_script_loader.cpp @@ -32,6 +32,7 @@ void AddSC_action_ip_logger(); // location: scripts\World\action_ip_logger.cpp void AddSC_player_scripts(); void AddSC_npc_stave_of_ancients(); void AddSC_server_mail(); +void AddSC_transport_zeppelins(); // The name of this function should match: // void Add${NameOfDirectory}Scripts() @@ -53,4 +54,5 @@ void AddWorldScripts() AddSC_player_scripts(); AddSC_npc_stave_of_ancients(); AddSC_server_mail(); + AddSC_transport_zeppelins(); }