diff options
-rw-r--r-- | sql/updates/world/master/2021_12_18_22_world_2019_08_06_05_world.sql | 30 | ||||
-rw-r--r-- | src/server/game/Miscellaneous/Language.h | 16 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_go.cpp | 160 |
3 files changed, 170 insertions, 36 deletions
diff --git a/sql/updates/world/master/2021_12_18_22_world_2019_08_06_05_world.sql b/sql/updates/world/master/2021_12_18_22_world_2019_08_06_05_world.sql new file mode 100644 index 00000000000..a8448d07945 --- /dev/null +++ b/sql/updates/world/master/2021_12_18_22_world_2019_08_06_05_world.sql @@ -0,0 +1,30 @@ +-- +DELETE FROM `trinity_string` WHERE `entry` BETWEEN 1205 AND 1211; +INSERT INTO `trinity_string` (`entry`,`content_default`) VALUES +(1205, 'No bosses were found matching your input.'), +(1206, 'Multiple bosses match your input - please be more specific:'), +(1207, '|- #%05u \'%s\' (%s)'), +(1208, 'Multiple spawn points exist for boss \'%s\' (creature #%05u) - go to one using .go creature:'), +(1209, '|- %06llu (map #%03u \'%s\' at %s)'), +(1210, 'Failed to teleport you to spawn point %llu for boss \'%s\' (creature #%05u) - are you missing an attunement for map \'%s\'?'), +(1211, 'Teleported you to spawn point for boss \'%s\' (creature #%05u, spawnid %llu)'); + +DELETE FROM `trinity_string` WHERE `entry` BETWEEN 1189 AND 1198; +INSERT INTO `trinity_string` (`entry`,`content_default`) VALUES +(1189, 'No instances were found matching your input.'), +(1190, 'Multiple instances match your input - please be more specific:'), +(1191,'|- \'%s\' (map #%03u, %s)'), +-- 1192 is newly unused +(1193,'Could not find entrance portal for map \'%s\' (map #%03u)'), +(1194,'Could not find exit portal for map \'%s\' (map #%03u)'), +(1195,'Teleported you to the entrance of \'%s\' (map #%03u)'), +(1196,'Teleported you to the start of \'%s\' (map #%03u)'), +(1197,'Failed to teleport you to the entrance of \'%s\' (map #%03u) - are you missing an attunement for map \'%s\' (#%03u)?'), +(1198,'Failed to teleport you to the start of \'%s\' (map #%03u) - are you missing an attunement for the instance?'); + +DELETE FROM `command` WHERE `name` IN ('go boss','go instance'); +INSERT INTO `command` (`name`,`permission`,`help`) VALUES +('go instance', 377, 'Syntax: .go instance <part(s) of name> +Teleport yourself to the instance whose name and/or script name best matches the specified search string(s).'), +('go boss', 377, 'Syntax: .go boss <part(s) of name> +Teleport yourself to the dungeon boss whose name and/or script name best matches the specified search string(s).'); diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 23b7ceda1fc..a1f167ba02d 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -961,7 +961,7 @@ enum TrinityStrings LANG_COMMAND_NO_INSTANCES_MATCH = 1189, LANG_COMMAND_MULTIPLE_INSTANCES_MATCH = 1190, LANG_COMMAND_MULTIPLE_INSTANCES_ENTRY = 1191, - LANG_COMMAND_MAP_NOT_INSTANCE = 1192, + // 1192 unused LANG_COMMAND_INSTANCE_NO_ENTRANCE = 1193, LANG_COMMAND_INSTANCE_NO_EXIT = 1194, LANG_COMMAND_WENT_TO_INSTANCE_GATE = 1195, @@ -977,13 +977,13 @@ enum TrinityStrings LANG_DEBUG_AREATRIGGER_OFF = 1203, LANG_DEBUG_AREATRIGGER_ENTERED = 1204, - LANG_COMMAND_NO_BOSSES_MATCH = 1205, // 3.3.5 RESERVED - LANG_COMMAND_MULTIPLE_BOSSES_MATCH = 1206, // 3.3.5 RESERVED - LANG_COMMAND_MULTIPLE_BOSSES_ENTRY = 1207, // 3.3.5 RESERVED - LANG_COMMAND_BOSS_MULTIPLE_SPAWNS = 1208, // 3.3.5 RESERVED - LANG_COMMAND_BOSS_MULTIPLE_SPAWN_ETY = 1209, // 3.3.5 RESERVED - LANG_COMMAND_GO_BOSS_FAILED = 1210, // 3.3.5 RESERVED - LANG_COMMAND_WENT_TO_BOSS = 1211, // 3.3.5 RESERVED + LANG_COMMAND_NO_BOSSES_MATCH = 1205, + LANG_COMMAND_MULTIPLE_BOSSES_MATCH = 1206, + LANG_COMMAND_MULTIPLE_BOSSES_ENTRY = 1207, + LANG_COMMAND_BOSS_MULTIPLE_SPAWNS = 1208, + LANG_COMMAND_BOSS_MULTIPLE_SPAWN_ETY = 1209, + LANG_COMMAND_GO_BOSS_FAILED = 1210, + LANG_COMMAND_WENT_TO_BOSS = 1211, LANG_GUILD_INFO_LEVEL = 1212, LANG_ACCOUNT_BNET_LINKED = 1213, LANG_ACCOUNT_OR_BNET_DOES_NOT_EXIST = 1214, diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index c3ad199a587..98d599e2720 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -24,6 +24,7 @@ EndScriptData */ #include "ScriptMgr.h" #include "Chat.h" +#include "Containers.h" #include "DatabaseEnv.h" #include "DB2Stores.h" #include "Language.h" @@ -73,7 +74,8 @@ public: { "complaintticket", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTicketCommand<ComplaintTicket>, "" }, { "suggestionticket", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTicketCommand<SuggestionTicket>, "" }, { "offset", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoOffsetCommand, "" }, - { "instance", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoInstanceCommand, "" } + { "instance", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoInstanceCommand, "" }, + { "boss", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoBossCommand, "" } }; static std::vector<ChatCommand> commandTable = @@ -461,19 +463,22 @@ public: static bool HandleGoInstanceCommand(ChatHandler* handler, std::vector<std::string> const& labels) { - uint32 mapid = 0; + if (labels.empty()) + return false; - std::multimap<uint32, std::pair<uint32, std::string>> matches; + // #matched labels -> (mapid, scriptname) + std::multimap<uint32, std::tuple<uint16, char const*, char const*>> matches; for (auto const& pair : sObjectMgr->GetInstanceTemplates()) { uint32 count = 0; std::string const& scriptName = sObjectMgr->GetScriptName(pair.second.ScriptId); + char const* mapName = ASSERT_NOTNULL(sMapStore.LookupEntry(pair.first))->MapName[handler->GetSessionDbcLocale()]; for (auto const& label : labels) if (StringContainsStringI(scriptName, label)) ++count; if (count) - matches.emplace(count, decltype(matches)::mapped_type({ pair.first, scriptName })); + matches.emplace(count, decltype(matches)::mapped_type(pair.first, mapName, scriptName.c_str())); } if (matches.empty()) { @@ -481,30 +486,23 @@ public: handler->SetSentErrorMessage(true); return false; } - auto it = matches.rbegin(); - uint32 maxCount = it->first; - mapid = it->second.first; - if (++it != matches.rend() && it->first == maxCount) + + auto it = matches.crbegin(), end = matches.crend(); + uint32 const maxCount = it->first; + if ((++it) != end && it->first == maxCount) { handler->SendSysMessage(LANG_COMMAND_MULTIPLE_INSTANCES_MATCH); --it; do - handler->PSendSysMessage(LANG_COMMAND_MULTIPLE_INSTANCES_ENTRY, it->second.first, it->second.second); - while (++it != matches.rend() && it->first == maxCount); + handler->PSendSysMessage(LANG_COMMAND_MULTIPLE_INSTANCES_ENTRY, std::get<1>(it->second), std::get<0>(it->second), std::get<2>(it->second)); + while (((++it) != end) && (it->first == maxCount)); handler->SetSentErrorMessage(true); return false; } - ASSERT(mapid); - - InstanceTemplate const* temp = sObjectMgr->GetInstanceTemplate(mapid); - if (!temp) - { - handler->PSendSysMessage(LANG_COMMAND_MAP_NOT_INSTANCE, mapid); - handler->SetSentErrorMessage(true); - return false; - } - std::string const& scriptname = sObjectMgr->GetScriptName(temp->ScriptId); + it = matches.crbegin(); + uint32 const mapId = std::get<0>(it->second); + char const* const mapName = std::get<1>(it->second); Player* player = handler->GetSession()->GetPlayer(); if (player->IsInFlight()) @@ -513,36 +511,142 @@ public: player->SaveRecallPosition(); // try going to entrance - if (AreaTriggerStruct const* exit = sObjectMgr->GetGoBackTrigger(mapid)) + if (AreaTriggerStruct const* exit = sObjectMgr->GetGoBackTrigger(mapId)) { if (player->TeleportTo(exit->target_mapId, exit->target_X, exit->target_Y, exit->target_Z, exit->target_Orientation + M_PI)) { - handler->PSendSysMessage(LANG_COMMAND_WENT_TO_INSTANCE_GATE, mapid, scriptname); + handler->PSendSysMessage(LANG_COMMAND_WENT_TO_INSTANCE_GATE, mapName, mapId); return true; } else - handler->PSendSysMessage(LANG_COMMAND_GO_INSTANCE_GATE_FAILED, mapid, scriptname, exit->target_mapId); + { + uint32 const parentMapId = exit->target_mapId; + char const* const parentMapName = ASSERT_NOTNULL(sMapStore.LookupEntry(parentMapId))->MapName[handler->GetSessionDbcLocale()]; + handler->PSendSysMessage(LANG_COMMAND_GO_INSTANCE_GATE_FAILED, mapName, mapId, parentMapName, parentMapId); + } } else - handler->PSendSysMessage(LANG_COMMAND_INSTANCE_NO_EXIT, mapid, scriptname); + handler->PSendSysMessage(LANG_COMMAND_INSTANCE_NO_EXIT, mapName, mapId); // try going to start - if (AreaTriggerStruct const* entrance = sObjectMgr->GetMapEntranceTrigger(mapid)) + if (AreaTriggerStruct const* entrance = sObjectMgr->GetMapEntranceTrigger(mapId)) { if (player->TeleportTo(entrance->target_mapId, entrance->target_X, entrance->target_Y, entrance->target_Z, entrance->target_Orientation)) { - handler->PSendSysMessage(LANG_COMMAND_WENT_TO_INSTANCE_START, mapid, scriptname); + handler->PSendSysMessage(LANG_COMMAND_WENT_TO_INSTANCE_START, mapName, mapId); return true; } else - handler->PSendSysMessage(LANG_COMMAND_GO_INSTANCE_START_FAILED, mapid, scriptname); + handler->PSendSysMessage(LANG_COMMAND_GO_INSTANCE_START_FAILED, mapName, mapId); } else - handler->PSendSysMessage(LANG_COMMAND_INSTANCE_NO_ENTRANCE, mapid, scriptname); + handler->PSendSysMessage(LANG_COMMAND_INSTANCE_NO_ENTRANCE, mapName, mapId); handler->SetSentErrorMessage(true); return false; } + + static bool HandleGoBossCommand(ChatHandler* handler, std::vector<std::string> const& needles) + { + if (needles.empty()) + return false; + + std::multimap<uint32, CreatureTemplate const*> matches; + std::unordered_map<uint32, std::vector<CreatureData const*>> spawnLookup; + + // find all boss flagged mobs that match our needles + for (auto const& pair : sObjectMgr->GetCreatureTemplates()) + { + CreatureTemplate const& data = pair.second; + if (!(data.flags_extra & CREATURE_FLAG_EXTRA_DUNGEON_BOSS)) + continue; + + uint32 count = 0; + std::string const& scriptName = sObjectMgr->GetScriptName(data.ScriptID); + for (auto const& label : needles) + if (StringContainsStringI(scriptName, label) || StringContainsStringI(data.Name, label)) + ++count; + + if (count) + { + matches.emplace(count, &data); + (void)spawnLookup[data.Entry]; // inserts default-constructed vector + } + } + + if (!matches.empty()) + { + // find the spawn points of any matches + for (auto const& pair : sObjectMgr->GetAllCreatureData()) + { + CreatureData const& data = pair.second; + auto it = spawnLookup.find(data.id); + if (it != spawnLookup.end()) + it->second.push_back(&data); + } + + // remove any matches without spawns + Trinity::Containers::EraseIf(matches, [&spawnLookup](decltype(matches)::value_type const& pair) { return spawnLookup[pair.second->Entry].empty(); }); + } + + // check if we even have any matches left + if (matches.empty()) + { + handler->SendSysMessage(LANG_COMMAND_NO_BOSSES_MATCH); + handler->SetSentErrorMessage(true); + return false; + } + + // see if we have multiple equal matches left + auto it = matches.crbegin(), end = matches.crend(); + uint32 const maxCount = it->first; + if ((++it) != end && it->first == maxCount) + { + handler->SendSysMessage(LANG_COMMAND_MULTIPLE_BOSSES_MATCH); + --it; + do + handler->PSendSysMessage(LANG_COMMAND_MULTIPLE_BOSSES_ENTRY, it->second->Entry, it->second->Name.c_str(), sObjectMgr->GetScriptName(it->second->ScriptID).c_str()); + while (((++it) != end) && (it->first == maxCount)); + handler->SetSentErrorMessage(true); + return false; + } + + CreatureTemplate const* const boss = matches.crbegin()->second; + std::vector<CreatureData const*> const& spawns = spawnLookup[boss->Entry]; + ASSERT(!spawns.empty()); + + if (spawns.size() > 1) + { + handler->PSendSysMessage(LANG_COMMAND_BOSS_MULTIPLE_SPAWNS, boss->Name.c_str(), boss->Entry); + for (CreatureData const* spawn : spawns) + { + uint32 const mapId = spawn->spawnPoint.GetMapId(); + MapEntry const* const map = ASSERT_NOTNULL(sMapStore.LookupEntry(mapId)); + handler->PSendSysMessage(LANG_COMMAND_BOSS_MULTIPLE_SPAWN_ETY, spawn->spawnId, mapId, map->MapName[handler->GetSessionDbcLocale()], spawn->spawnPoint.GetPosition().ToString().c_str()); + } + handler->SetSentErrorMessage(true); + return false; + } + + Player* player = handler->GetSession()->GetPlayer(); + if (player->IsInFlight()) + player->FinishTaxiFlight(); + else + player->SaveRecallPosition(); + + CreatureData const* const spawn = spawns.front(); + uint32 const mapId = spawn->spawnPoint.GetMapId(); + if (!player->TeleportTo(spawn->spawnPoint)) + { + char const* const mapName = ASSERT_NOTNULL(sMapStore.LookupEntry(mapId))->MapName[handler->GetSessionDbcLocale()]; + handler->PSendSysMessage(LANG_COMMAND_GO_BOSS_FAILED, spawn->spawnId, boss->Name.c_str(), boss->Entry, mapName); + handler->SetSentErrorMessage(true); + return false; + } + + handler->PSendSysMessage(LANG_COMMAND_WENT_TO_BOSS, boss->Name.c_str(), boss->Entry, spawn->spawnId); + return true; + } }; void AddSC_go_commandscript() |