/*
* 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 "Chat.h"
#include "CommandScript.h"
#include "GameGraveyard.h"
#include "Language.h"
#include "MapMgr.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "TicketMgr.h"
#include "boost/algorithm/string.hpp"
#include
using namespace Acore::ChatCommands;
class go_commandscript : public CommandScript
{
public:
go_commandscript() : CommandScript("go_commandscript") { }
ChatCommandTable GetCommands() const override
{
static ChatCommandTable goCommandTable =
{
{ "creature", HandleGoCreatureSpawnIdCommand, SEC_MODERATOR, Console::No },
{ "creature id", HandleGoCreatureCIdCommand, SEC_MODERATOR, Console::No },
{ "creature name", HandleGoCreatureNameCommand, SEC_MODERATOR, Console::No },
{ "gameobject", HandleGoGameObjectSpawnIdCommand, SEC_MODERATOR, Console::No },
{ "gameobject id", HandleGoGameObjectGOIdCommand, SEC_MODERATOR, Console::No },
{ "graveyard", HandleGoGraveyardCommand, SEC_MODERATOR, Console::No },
{ "grid", HandleGoGridCommand, SEC_MODERATOR, Console::No },
{ "taxinode", HandleGoTaxinodeCommand, SEC_MODERATOR, Console::No },
{ "trigger", HandleGoTriggerCommand, SEC_MODERATOR, Console::No },
{ "zonexy", HandleGoZoneXYCommand, SEC_MODERATOR, Console::No },
{ "xyz", HandleGoXYZCommand, SEC_MODERATOR, Console::No },
{ "ticket", HandleGoTicketCommand, SEC_GAMEMASTER, Console::No },
{ "quest", HandleGoQuestCommand, SEC_MODERATOR, Console::No },
};
static ChatCommandTable commandTable =
{
{ "go", goCommandTable }
};
return commandTable;
}
static bool DoTeleport(ChatHandler* handler, Position pos, uint32 mapId = MAPID_INVALID)
{
Player* player = handler->GetSession()->GetPlayer();
if (mapId == MAPID_INVALID)
mapId = player->GetMapId();
if (!MapMgr::IsValidMapCoord(mapId, pos) || sObjectMgr->IsTransportMap(mapId))
{
handler->SendErrorMessage(LANG_INVALID_TARGET_COORD, pos.GetPositionX(), pos.GetPositionY(), mapId);
return false;
}
// stop flight if need
if (player->IsInFlight())
{
player->GetMotionMaster()->MovementExpired();
player->CleanupAfterTaxiFlight();
}
// save only in non-flight case
else
player->SaveRecallPosition();
player->TeleportTo({ mapId, pos });
return true;
}
static bool HandleGoCreatureCIdCommand(ChatHandler* handler, Variant, uint32> cId, Optional _pos)
{
uint32 pos = 1;
if (_pos)
{
pos = *_pos;
if (pos < 1)
{
handler->SendErrorMessage(LANG_COMMAND_FACTION_INVPARAM, pos);
return false;
}
}
std::vector spawnpoints = GetCreatureDataList(*cId);
if (spawnpoints.empty())
{
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
return false;
}
if (spawnpoints.size() < pos)
{
handler->SendErrorMessage(LANG_COMMAND_GONOTENOUGHSPAWNS, pos, spawnpoints.size());
return false;
}
CreatureData const* spawnpoint = spawnpoints[--pos];
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
static bool HandleGoCreatureSpawnIdCommand(ChatHandler* handler, Variant, ObjectGuid::LowType> spawnId)
{
CreatureData const* spawnpoint = sObjectMgr->GetCreatureData(spawnId);
if (!spawnpoint)
{
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
return false;
}
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
static bool HandleGoCreatureNameCommand(ChatHandler* handler, Tail name)
{
if (!name.data())
return false;
// Make sure we don't pass double quotes into the SQL query. Otherwise it causes a MySQL error
std::string str = name.data(); // Making subtractions to the last character does not with in string_view
if (str.front() == '"')
str = str.substr(1);
if (str.back() == '"')
str = str.substr(0, str.size() - 1);
QueryResult result = WorldDatabase.Query("SELECT entry FROM creature_template WHERE name = \"{}\" LIMIT 1", str);
if (!result)
{
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
return false;
}
uint32 entry = result->Fetch()[0].Get();
CreatureData const* spawnpoint = GetCreatureData(handler, entry);
if (!spawnpoint)
{
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
return false;
}
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
static bool HandleGoGameObjectSpawnIdCommand(ChatHandler* handler, uint32 spawnId)
{
GameObjectData const* spawnpoint = sObjectMgr->GetGameObjectData(spawnId);
if (!spawnpoint)
{
handler->SendErrorMessage(LANG_COMMAND_GOOBJNOTFOUND);
return false;
}
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
static bool HandleGoGameObjectGOIdCommand(ChatHandler* handler, uint32 goId, Optional _pos)
{
uint32 pos = 1;
if (_pos)
{
pos = *_pos;
if (pos < 1)
{
handler->SendErrorMessage(LANG_COMMAND_FACTION_INVPARAM, pos);
return false;
}
}
std::vector spawnpoints = GetGameObjectDataList(goId);
if (spawnpoints.empty())
{
handler->SendErrorMessage(LANG_COMMAND_GOOBJNOTFOUND);
return false;
}
if (spawnpoints.size() < pos)
{
handler->SendErrorMessage(LANG_COMMAND_GONOTENOUGHSPAWNS, pos, spawnpoints.size());
return false;
}
GameObjectData const* spawnpoint = spawnpoints[--pos];
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
static bool HandleGoGraveyardCommand(ChatHandler* handler, uint32 gyId)
{
GraveyardStruct const* gy = sGraveyard->GetGraveyard(gyId);
if (!gy)
{
handler->SendErrorMessage(LANG_COMMAND_GRAVEYARDNOEXIST, gyId);
return false;
}
if (!MapMgr::IsValidMapCoord(gy->Map, gy->x, gy->y, gy->z))
{
handler->SendErrorMessage(LANG_INVALID_TARGET_COORD, gy->x, gy->y, gy->Map);
return false;
}
Player* player = handler->GetSession()->GetPlayer();
// stop flight if need
if (player->IsInFlight())
{
player->GetMotionMaster()->MovementExpired();
player->CleanupAfterTaxiFlight();
}
// save only in non-flight case
else
player->SaveRecallPosition();
player->TeleportTo(gy->Map, gy->x, gy->y, gy->z, player->GetOrientation());
return true;
}
//teleport to grid
static bool HandleGoGridCommand(ChatHandler* handler, float gridX, float gridY, Optional oMapId)
{
Player* player = handler->GetSession()->GetPlayer();
uint32 mapId = oMapId.value_or(player->GetMapId());
// center of grid
float x = (gridX - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
float y = (gridY - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
if (!MapMgr::IsValidMapCoord(mapId, x, y))
{
handler->SendErrorMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
return false;
}
// stop flight if need
if (player->IsInFlight())
{
player->GetMotionMaster()->MovementExpired();
player->CleanupAfterTaxiFlight();
}
// save only in non-flight case
else
player->SaveRecallPosition();
Map const* map = sMapMgr->CreateBaseMap(mapId);
float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
player->TeleportTo(mapId, x, y, z, player->GetOrientation());
return true;
}
static bool HandleGoTaxinodeCommand(ChatHandler* handler, Variant, uint32> nodeId)
{
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(nodeId);
if (!node)
{
handler->SendErrorMessage(LANG_COMMAND_GOTAXINODENOTFOUND, uint32(nodeId));
return false;
}
return DoTeleport(handler, { node->x, node->y, node->z }, node->map_id);
}
static bool HandleGoTriggerCommand(ChatHandler* handler, Variant, uint32> areaTriggerId)
{
AreaTrigger const* at = sObjectMgr->GetAreaTrigger(areaTriggerId);
if (!at)
{
handler->SendErrorMessage(LANG_COMMAND_GOAREATRNOTFOUND, uint32(areaTriggerId));
return false;
}
return DoTeleport(handler, { at->x, at->y, at->z }, at->map);
}
//teleport at coordinates
static bool HandleGoZoneXYCommand(ChatHandler* handler, float x, float y, Optional, uint32>> areaIdArg)
{
Player* player = handler->GetSession()->GetPlayer();
uint32 areaId = areaIdArg ? *areaIdArg : player->GetZoneId();
AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId);
if (x < 0 || x > 100 || y < 0 || y > 100 || !areaEntry)
{
handler->SendErrorMessage(LANG_INVALID_ZONE_COORD, x, y, areaId);
return false;
}
// update to parent zone if exist (client map show only zones without parents)
AreaTableEntry const* zoneEntry = areaEntry->zone ? sAreaTableStore.LookupEntry(areaEntry->zone) : areaEntry;
ASSERT(zoneEntry);
Map const* map = sMapMgr->CreateBaseMap(zoneEntry->mapid);
if (map->Instanceable())
{
handler->SendErrorMessage(LANG_INVALID_ZONE_MAP, areaEntry->ID, areaEntry->area_name[handler->GetSessionDbcLocale()], map->GetId(), map->GetMapName());
return false;
}
Zone2MapCoordinates(x, y, zoneEntry->ID);
if (!MapMgr::IsValidMapCoord(zoneEntry->mapid, x, y))
{
handler->SendErrorMessage(LANG_INVALID_TARGET_COORD, x, y, zoneEntry->mapid);
return false;
}
// stop flight if need
if (player->IsInFlight())
{
player->GetMotionMaster()->MovementExpired();
player->CleanupAfterTaxiFlight();
}
// save only in non-flight case
else
player->SaveRecallPosition();
float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
player->TeleportTo(zoneEntry->mapid, x, y, z, player->GetOrientation());
return true;
}
/**
* @brief Teleports the GM to the specified world coordinates, optionally specifying map ID and orientation.
*
* @param handler The ChatHandler that is handling the command.
* @param args The coordinates to teleport to in format "x y z [mapId [orientation]]".
* @return true The command was successful.
* @return false The command was unsuccessful (show error or syntax)
*/
static bool HandleGoXYZCommand(ChatHandler* handler, Tail args)
{
std::wstring wInputCoords;
if (!Utf8toWStr(args, wInputCoords))
{
return false;
}
// extract float and integer values from the input
std::vector locationValues;
std::wregex floatRegex(L"(-?\\d+(?:\\.\\d+)?)");
std::wsregex_iterator floatRegexIterator(wInputCoords.begin(), wInputCoords.end(), floatRegex);
std::wsregex_iterator end;
while (floatRegexIterator != end)
{
std::wsmatch match = *floatRegexIterator;
std::wstring matchStr = match.str();
// try to convert the match to a float
try
{
locationValues.push_back(std::stof(matchStr));
}
// if the match is not a float, do not add it to the vector
catch (std::invalid_argument const&){}
++floatRegexIterator;
}
// X and Y are required
if (locationValues.size() < 2)
{
return false;
}
Player* player = handler->GetSession()->GetPlayer();
uint32 mapId = locationValues.size() >= 4 ? uint32(locationValues[3]) : player->GetMapId();
float x = locationValues[0];
float y = locationValues[1];
if (!sMapStore.LookupEntry(mapId) || !MapMgr::IsValidMapCoord(mapId, x, y))
{
handler->SendErrorMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
return false;
}
Map const* map = sMapMgr->CreateBaseMap(mapId);
float z = locationValues.size() >= 3 ? locationValues[2] : std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y));
// map ID (locationValues[3]) already handled above
float o = locationValues.size() >= 5 ? locationValues[4] : player->GetOrientation();
if (!MapMgr::IsValidMapCoord(mapId, x, y, z, o))
{
handler->SendErrorMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
return false;
}
return DoTeleport(handler, { x, y, z, o }, mapId);
}
static bool HandleGoTicketCommand(ChatHandler* handler, uint32 ticketId)
{
GmTicket* ticket = sTicketMgr->GetTicket(ticketId);
if (!ticket)
{
handler->SendSysMessage(LANG_COMMAND_TICKETNOTEXIST);
return true;
}
Player* player = handler->GetSession()->GetPlayer();
// stop flight if need
if (player->IsInFlight())
{
player->GetMotionMaster()->MovementExpired();
player->CleanupAfterTaxiFlight();
}
// save only in non-flight case
else
player->SaveRecallPosition();
ticket->TeleportTo(player);
return true;
}
static bool HandleGoQuestCommand(ChatHandler* handler, std::string_view type, Quest const* quest)
{
uint32 entry = quest->GetQuestId();
if (type == "starter")
{
QuestRelations* qr = sObjectMgr->GetCreatureQuestRelationMap();
for (auto itr = qr->begin(); itr != qr->end(); ++itr)
{
if (itr->second == entry)
{
CreatureData const* spawnpoint = GetCreatureData(handler, itr->first);
if (!spawnpoint)
{
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
return false;
}
// We've found a creature, teleport to it.
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
}
qr = sObjectMgr->GetGOQuestRelationMap();
for (auto itr = qr->begin(); itr != qr->end(); ++itr)
{
if (itr->second == entry)
{
GameObjectData const* spawnpoint = GetGameObjectData(handler, itr->first);
if (!spawnpoint)
{
handler->SendErrorMessage(LANG_COMMAND_GOOBJNOTFOUND);
return false;
}
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
}
}
else if (type == "ender")
{
QuestRelations* qr = sObjectMgr->GetCreatureQuestInvolvedRelationMap();
for (auto itr = qr->begin(); itr != qr->end(); ++itr)
{
if (itr->second == entry)
{
CreatureData const* spawnpoint = GetCreatureData(handler, itr->first);
if (!spawnpoint)
{
handler->SendErrorMessage(LANG_COMMAND_GOCREATNOTFOUND);
return false;
}
// We've found a creature, teleport to it.
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
}
qr = sObjectMgr->GetGOQuestInvolvedRelationMap();
for (auto itr = qr->begin(); itr != qr->end(); ++itr)
{
if (itr->second == entry)
{
GameObjectData const* spawnpoint = GetGameObjectData(handler, itr->first);
if (!spawnpoint)
{
handler->SendErrorMessage(LANG_COMMAND_GOOBJNOTFOUND);
return false;
}
return DoTeleport(handler, { spawnpoint->posX, spawnpoint->posY, spawnpoint->posZ }, spawnpoint->mapid);
}
}
}
else
{
handler->SendErrorMessage(LANG_CMD_GOQUEST_INVALID_SYNTAX);
return false;
}
return false;
}
static CreatureData const* GetCreatureData(ChatHandler* handler, uint32 entry)
{
CreatureData const* spawnpoint = nullptr;
for (auto const& pair : sObjectMgr->GetAllCreatureData())
{
if (pair.second.id1 != entry)
{
continue;
}
if (!spawnpoint)
{
spawnpoint = &pair.second;
}
else
{
handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
break;
}
}
return spawnpoint;
}
static std::vector GetCreatureDataList(uint32 entry)
{
std::vector spawnpoints;
for (auto const& pair : sObjectMgr->GetAllCreatureData())
{
if (pair.second.id1 != entry)
{
continue;
}
spawnpoints.emplace_back(&pair.second);
}
return spawnpoints;
}
static GameObjectData const* GetGameObjectData(ChatHandler* handler, uint32 entry)
{
GameObjectData const* spawnpoint = nullptr;
for (auto const& pair : sObjectMgr->GetAllGOData())
{
if (pair.second.id != entry)
{
continue;
}
if (!spawnpoint)
{
spawnpoint = &pair.second;
}
else
{
handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
break;
}
}
return spawnpoint;
}
static std::vector GetGameObjectDataList(uint32 entry)
{
std::vector spawnpoints;
for (auto const& pair : sObjectMgr->GetAllGOData())
{
if (pair.second.id != entry)
{
continue;
}
spawnpoints.emplace_back(&pair.second);
}
return spawnpoints;
}
};
void AddSC_go_commandscript()
{
new go_commandscript();
}