diff --git a/data/sql/updates/pending_db_world/rev_1785371667750135300.sql b/data/sql/updates/pending_db_world/rev_1785371667750135300.sql new file mode 100644 index 000000000..2a7ddf0b6 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1785371667750135300.sql @@ -0,0 +1,15 @@ +-- +-- Zeppelin, Alliance - Westguard Keep to Shattered Straits (taxi path 727) +UPDATE `gameobject_template` SET `ScriptName` = 'go_transport_westguard_zeppelin' WHERE (`entry` = 186371); + +-- Harrowmeiser - dock announcements, fired from the taxi arrival event 15431 +DELETE FROM `creature_text` WHERE (`CreatureID` = 23823); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(23823, 0, 0, 'My zeppelin has docked. Despite my being held captive, and the sorry state that she\'s in, she\'ll be departing the dock again in two minutes.', 14, 0, 100, 0, 0, 0, 22336, 0, 'Harrowmeiser - Arrival'), +(23823, 1, 0, 'The zeppelin\'s leaving for a tour around the bay in less than one minute! Oh, and if anyone feels like setting me free, I\'m on the dock.', 14, 0, 100, 0, 0, 0, 22337, 0, 'Harrowmeiser - Departure'); + +-- Harrowmeiser and Bombardier Petrov - gossip text picked from the zeppelin's path +-- progress, which also pushes the world state that npc_text 11322 and 11332 substitute +-- into "in less than $3078w minutes" +UPDATE `creature_template` SET `ScriptName` = 'npc_harrowmeiser' WHERE (`entry` = 23823); +UPDATE `creature_template` SET `ScriptName` = 'npc_bombardier_petrov' WHERE (`entry` = 23895); diff --git a/src/server/scripts/World/transport_zeppelin.h b/src/server/scripts/World/transport_zeppelin.h index dd252a14e..6b9b6985d 100644 --- a/src/server/scripts/World/transport_zeppelin.h +++ b/src/server/scripts/World/transport_zeppelin.h @@ -36,6 +36,7 @@ enum ZeppelinEvent EVENT_UC_TO_OG_DEPARTURE = 15321, EVENT_UC_TO_GROMGOL_DEPARTURE = 15313, EVENT_GROMGOL_TO_UC_DEPARTURE = 15315, + EVENT_WK_DEPARTURE = 15430, }; enum ZeppelinMaster @@ -55,6 +56,11 @@ enum ZeppelinMaster NPC_KRENDLE_BIGPOCKETS = 34766, }; +enum ZeppelinTransport +{ + GO_WESTGUARD_ZEPPELIN = 186371, +}; + const float SEARCH_RANGE_ZEPPELIN_MASTER = 32.0f; enum ZeppelinPassenger diff --git a/src/server/scripts/World/transport_zeppelins.cpp b/src/server/scripts/World/transport_zeppelins.cpp index 3edbceca9..ca9121ccd 100644 --- a/src/server/scripts/World/transport_zeppelins.cpp +++ b/src/server/scripts/World/transport_zeppelins.cpp @@ -15,8 +15,15 @@ * with this program. If not, see . */ +#include "CreatureScript.h" #include "GameObjectAI.h" #include "GameObjectScript.h" +#include "Map.h" +#include "Player.h" +#include "ScriptedGossip.h" +#include "TaskScheduler.h" +#include "Transport.h" +#include "TransportMgr.h" #include "WorldState.h" #include "transport_zeppelin.h" @@ -101,9 +108,166 @@ struct go_transport_the_purple_princess : GameObjectAI } }; +// 186371 Zeppelin - Westguard Keep to Shattered Straits +struct go_transport_westguard_zeppelin : GameObjectAI +{ + go_transport_westguard_zeppelin(GameObject* object) : GameObjectAI(object) { }; + + void EventInform(uint32 eventId) override + { + if (eventId != EVENT_WK_ARRIVAL) + return; + + if (Creature* creature = me->FindNearestCreature(NPC_HARROWMEISER, SEARCH_RANGE_ZEPPELIN_MASTER)) + creature->AI()->Talk(0); + + // the dock is covered by two stop frames of 60s each, so EVENT_WK_DEPARTURE + // only fires two minutes later - warn one minute before it does + _scheduler.Schedule(1min, [this](TaskContext /*context*/) + { + if (Creature* creature = me->FindNearestCreature(NPC_HARROWMEISER, SEARCH_RANGE_ZEPPELIN_MASTER)) + creature->AI()->Talk(1); + }); + } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } + +private: + TaskScheduler _scheduler; +}; + +enum WestguardZeppelinGossip +{ + GOSSIP_TEXT_PETROV_EN_ROUTE = 11322, + GOSSIP_TEXT_PETROV_ARRIVING = 11324, + GOSSIP_TEXT_HARROWMEISER_DOCKED = 11328, + GOSSIP_TEXT_HARROWMEISER_ARRIVING = 11330, + GOSSIP_TEXT_HARROWMEISER_EN_ROUTE = 11332, +}; + +// The two "en route" texts above read "in less than $3078w minutes". The client +// substitutes that token with the world state below, so it renders 0 unless a value +// has been pushed to the player first. +enum WestguardZeppelinWorldState +{ + WORLD_STATE_ZEPPELIN_ETA = 3078, +}; + +enum WestguardZeppelinState +{ + ZEPPELIN_STATE_EN_ROUTE, + ZEPPELIN_STATE_ARRIVING, + ZEPPELIN_STATE_DOCKED, +}; + +// Reports where the Westguard Keep zeppelin is in its path. While it is still touring the +// bay the remaining minutes are pushed to the player as WORLD_STATE_ZEPPELIN_ETA. +WestguardZeppelinState GetWestguardZeppelinState(Player* player, Map* map) +{ + MotionTransport const* zeppelin = nullptr; + for (Transport* transport : map->GetAllTransports()) + if (transport->GetEntry() == GO_WESTGUARD_ZEPPELIN) + { + zeppelin = transport->ToMotionTransport(); + break; + } + + if (!zeppelin || !zeppelin->GetPeriod()) + return ZEPPELIN_STATE_EN_ROUTE; + + uint32 dockArriveTime = 0; + uint32 dockDepartTime = 0; + for (KeyFrame const& frame : zeppelin->GetKeyFrames()) + { + if (frame.Node->arrivalEventID == EVENT_WK_ARRIVAL) + dockArriveTime = frame.ArriveTime; + else if (frame.Node->departureEventID == EVENT_WK_DEPARTURE) + dockDepartTime = frame.DepartureTime; + } + + if (!dockArriveTime) + return ZEPPELIN_STATE_EN_ROUTE; + + uint32 const period = zeppelin->GetPeriod(); + uint32 const timer = zeppelin->GetPathProgress() % period; + + // the dock is covered by the last and the first stop frame of the path, so the docked + // window normally wraps the end of one period into the start of the next + bool const docked = dockArriveTime > dockDepartTime + ? timer >= dockArriveTime || timer < dockDepartTime + : timer >= dockArriveTime && timer < dockDepartTime; + + if (docked) + return ZEPPELIN_STATE_DOCKED; + + // modular so it stays correct whichever way around the two stop frames sit + uint32 const msUntilArrival = (dockArriveTime + period - timer) % period; + if (msUntilArrival <= MINUTE * IN_MILLISECONDS) + return ZEPPELIN_STATE_ARRIVING; + + // the texts read "less than X minutes", so round up + player->SendUpdateWorldState(WORLD_STATE_ZEPPELIN_ETA, + (msUntilArrival + MINUTE * IN_MILLISECONDS - 1) / (MINUTE * IN_MILLISECONDS)); + + return ZEPPELIN_STATE_EN_ROUTE; +} + +// 23823 Harrowmeiser +class npc_harrowmeiser : public CreatureScript +{ +public: + npc_harrowmeiser() : CreatureScript("npc_harrowmeiser") { } + + bool OnGossipHello(Player* player, Creature* creature) override + { + uint32 textId = GOSSIP_TEXT_HARROWMEISER_EN_ROUTE; + switch (GetWestguardZeppelinState(player, creature->GetMap())) + { + case ZEPPELIN_STATE_DOCKED: + textId = GOSSIP_TEXT_HARROWMEISER_DOCKED; + break; + case ZEPPELIN_STATE_ARRIVING: + textId = GOSSIP_TEXT_HARROWMEISER_ARRIVING; + break; + default: + break; + } + + SendGossipMenuFor(player, textId, creature->GetGUID()); + return true; + } +}; + +// 23895 Bombardier Petrov +class npc_bombardier_petrov : public CreatureScript +{ +public: + npc_bombardier_petrov() : CreatureScript("npc_bombardier_petrov") { } + + bool OnGossipHello(Player* player, Creature* creature) override + { + if (creature->IsQuestGiver()) + player->PrepareQuestMenu(creature->GetGUID()); + + // he has no docked text of his own, "she's almost here" covers being at the dock too + uint32 const textId = GetWestguardZeppelinState(player, creature->GetMap()) == ZEPPELIN_STATE_EN_ROUTE + ? GOSSIP_TEXT_PETROV_EN_ROUTE + : GOSSIP_TEXT_PETROV_ARRIVING; + + SendGossipMenuFor(player, textId, creature->GetGUID()); + return true; + } +}; + void AddSC_transport_zeppelins() { RegisterGameObjectAI(go_transport_the_iron_eagle); RegisterGameObjectAI(go_transport_the_thundercaller); RegisterGameObjectAI(go_transport_the_purple_princess); + RegisterGameObjectAI(go_transport_westguard_zeppelin); + new npc_harrowmeiser(); + new npc_bombardier_petrov(); }