mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Scripts/Commands: New argument parsing methodology (PR #22363)
- Detect the arguments accepted by the command handler
- Tokenize out those arguments automatically and feed them to the handler
- Unmatched rest of the string can be accepted by trailing char const* or CommandArgs*
(cherry picked from commit 66a87c4642)
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
--
|
||||
DELETE FROM `command` WHERE `name` IN ('go creature', 'go creature id', 'go object', 'go gameobject', 'go gameobject id', 'go graveyard', 'go grid', 'go instance', 'go offset', 'go taxinode', 'go ticket', 'go trigger', 'go areatrigger', 'go xyz', 'go zonexy');
|
||||
INSERT INTO `command` (`name`,`permission`,`help`) VALUES
|
||||
('go creature', 377, 'Syntax: .go creature <spawnId>
|
||||
Teleport yourself to the creature spawn with the specified spawn ID.'),
|
||||
('go creature id', 377, 'Syntax: .go creature id <creatureId>
|
||||
Teleport yourself to the first spawn point for the specified creature ID.
|
||||
If multiple spawn points for the creature exist, teleport to the first one found.'),
|
||||
('go gameobject', 377, 'Syntax: .go gameobject <spawnId>
|
||||
Teleport yourself to the gameobject spawn with the specified spawn ID.'),
|
||||
('go gameobject id', 377, 'Syntax: .go gameobject id <goId>
|
||||
Teleport yourself to the first spawn point for the specified gameobject ID.
|
||||
If multiple spawn points for the gameobject exist, teleport to the first one found.'),
|
||||
('go graveyard', 377, 'Syntax: .go graveyard <graveyardId>
|
||||
Teleport yourself to the graveyard with the specified graveyard ID.'),
|
||||
('go grid', 377, 'Syntax: .go grid <gridX> <gridY> [<mapId>]
|
||||
Teleport yourself to center of grid at the provided indices in specified (or current) map.'),
|
||||
('go instance', 377, 'Syntax: .go instance <part of scriptname>
|
||||
Teleport yourself to the entrance of the instance matching the specified script name.'),
|
||||
('go offset', 377, 'Syntax: .go offset [<dForward> [<dSideways> [<dZ [<dO>]]]]
|
||||
Teleport yourself by the specified amount relative to your current position and orientation.'),
|
||||
('go taxinode', 377, 'Syntax: .go taxinode <nodeId>
|
||||
Teleport yourself to the specified taxi node.'),
|
||||
('go ticket', 377, 'Syntax: .go ticket <ticketId>
|
||||
Teleport yourself to the location at which the specified ticket was created.'),
|
||||
('go areatrigger', 377, 'Syntax: .go areatrigger <areatriggerId>
|
||||
Teleport yourself to the specified areatrigger\'s location.
|
||||
Note that you may end up at the trigger\'s target location if it is a teleport trigger.'),
|
||||
('go xyz', 377, 'Syntax: .go xyz <x> <y> [<z> [<mapid> [<o>]]]
|
||||
Teleport yourself to the specified location in the specified (or current) map.
|
||||
If no z coordinate is specified, defaults to ground/water level.'),
|
||||
('go zonexy', 377, 'Syntax: .go zonexy <x> <y> [<zone>]
|
||||
Teleport yourself to the given local (x,y) position in the specified (or current) zone.');
|
||||
@@ -22,6 +22,8 @@
|
||||
#include <utf8.h>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <cstdarg>
|
||||
#include <ctime>
|
||||
|
||||
@@ -755,6 +757,12 @@ bool StringToBool(std::string const& str)
|
||||
return lowerStr == "1" || lowerStr == "true" || lowerStr == "yes";
|
||||
}
|
||||
|
||||
bool StringContainsStringI(std::string const& haystack, std::string const& needle)
|
||||
{
|
||||
return haystack.end() !=
|
||||
std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), [](char c1, char c2) { return std::toupper(c1) == std::toupper(c2); });
|
||||
}
|
||||
|
||||
float DegToRad(float degrees)
|
||||
{
|
||||
return degrees * (2.f * float(M_PI) / 360.f);
|
||||
|
||||
@@ -352,6 +352,8 @@ std::array<uint8, Size> HexStrToByteArray(std::string const& str, bool reverse =
|
||||
TC_COMMON_API bool StringToBool(std::string const& str);
|
||||
TC_COMMON_API float DegToRad(float degrees);
|
||||
|
||||
TC_COMMON_API bool StringContainsStringI(std::string const& haystack, std::string const& needle);
|
||||
|
||||
// simple class for not-modifyable list
|
||||
template <typename T>
|
||||
class HookList final
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
// this namespace holds implementations of upcoming stdlib features that our c++ version doesn't have yet
|
||||
namespace advstd
|
||||
|
||||
@@ -38,11 +38,6 @@
|
||||
#include "WorldSession.h"
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
ChatCommand::ChatCommand(char const* name, uint32 permission, bool allowConsole, pHandler handler, std::string help, std::vector<ChatCommand> childCommands /*= std::vector<ChatCommand>()*/)
|
||||
: Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Handler(handler), Help(std::move(help)), ChildCommands(std::move(childCommands))
|
||||
{
|
||||
}
|
||||
|
||||
// Lazy loading of the command table cache from commands and the
|
||||
// ScriptMgr should be thread safe since the player commands,
|
||||
// cli commands and ScriptMgr updates are all dispatched one after
|
||||
@@ -309,12 +304,12 @@ bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, c
|
||||
}
|
||||
|
||||
// must be available and have handler
|
||||
if (!table[i].Handler || !isAvailable(table[i]))
|
||||
if (!table[i].HasHandler() || !isAvailable(table[i]))
|
||||
continue;
|
||||
|
||||
SetSentErrorMessage(false);
|
||||
// table[i].Name == "" is special case: send original command to handler
|
||||
if ((table[i].Handler)(this, table[i].Name[0] != '\0' ? text : oldtext))
|
||||
if (table[i](this, table[i].Name[0] != '\0' ? text : oldtext))
|
||||
{
|
||||
if (!m_session) // ignore console
|
||||
return true;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifndef TRINITYCORE_CHAT_H
|
||||
#define TRINITYCORE_CHAT_H
|
||||
|
||||
#include "ChatCommand.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "StringFormat.h"
|
||||
@@ -36,21 +37,6 @@ struct GameTele;
|
||||
|
||||
enum LocaleConstant : uint8;
|
||||
|
||||
class TC_GAME_API ChatCommand
|
||||
{
|
||||
typedef bool(*pHandler)(ChatHandler*, char const*);
|
||||
|
||||
public:
|
||||
ChatCommand(char const* name, uint32 permission, bool allowConsole, pHandler handler, std::string help, std::vector<ChatCommand> childCommands = std::vector<ChatCommand>());
|
||||
|
||||
char const* Name;
|
||||
uint32 Permission; // function pointer required correct align (use uint32)
|
||||
bool AllowConsole;
|
||||
pHandler Handler;
|
||||
std::string Help;
|
||||
std::vector<ChatCommand> ChildCommands;
|
||||
};
|
||||
|
||||
class TC_GAME_API ChatHandler
|
||||
{
|
||||
public:
|
||||
|
||||
279
src/server/game/Chat/ChatCommands/ChatCommand.h
Normal file
279
src/server/game/Chat/ChatCommands/ChatCommand.h
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_CHATCOMMAND_H
|
||||
#define TRINITY_CHATCOMMAND_H
|
||||
|
||||
#include "advstd.h"
|
||||
#include "ChatCommandArgs.h"
|
||||
#include "ChatCommandTags.h"
|
||||
#include "Define.h"
|
||||
#include "Errors.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Optional.h"
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
class ChatHandler;
|
||||
class CommandArgs;
|
||||
|
||||
template <typename T>
|
||||
struct CommandArgsConsumerSingle
|
||||
{
|
||||
using arginfo = Trinity::ChatCommands::ArgInfo<T>;
|
||||
static char const* TryConsumeTo(T& val, char const* args)
|
||||
{
|
||||
return arginfo::TryConsume(val, args);
|
||||
}
|
||||
};
|
||||
|
||||
struct CommandArgsVariantConsumer
|
||||
{
|
||||
template <typename V, typename T1, typename T2, typename... Ts>
|
||||
static char const* TryConsumeTo(V& val, char const* args)
|
||||
{
|
||||
T1 v;
|
||||
if (char const* next = CommandArgsConsumerSingle<T1>::TryConsumeTo(v, args))
|
||||
{
|
||||
val = std::move(v);
|
||||
return next;
|
||||
}
|
||||
else
|
||||
return TryConsumeTo<V, T2, Ts...>(val, args);
|
||||
}
|
||||
|
||||
template <typename V, typename T1>
|
||||
static char const* TryConsumeTo(V& val, char const* args)
|
||||
{
|
||||
T1 v;
|
||||
if (char const* next = CommandArgsConsumerSingle<T1>::TryConsumeTo(v, args))
|
||||
{
|
||||
val = std::move(v);
|
||||
return next;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
struct CommandArgsConsumerSingle<Trinity::ChatCommands::Variant<Ts...>>
|
||||
{
|
||||
static char const* TryConsumeTo(Trinity::ChatCommands::Variant<Ts...>& val, char const* args)
|
||||
{
|
||||
return CommandArgsVariantConsumer::TryConsumeTo<Trinity::ChatCommands::Variant<Ts...>, Ts...>(val, args);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct CommandArgsConsumerSingle<std::vector<T>>
|
||||
{
|
||||
static char const* TryConsumeTo(std::vector<T>& val, char const* args)
|
||||
{
|
||||
char const* last;
|
||||
val.clear();
|
||||
|
||||
do val.emplace_back();
|
||||
while ((args = CommandArgsConsumerSingle<T>::TryConsumeTo(val.back(), (last = args))));
|
||||
|
||||
val.pop_back();
|
||||
return last;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CommandArgsConsumerSingle<CommandArgs*>
|
||||
{
|
||||
static char const* TryConsumeTo(CommandArgs*&, char const* args) { return args; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct CommandArgsConsumerSingle<char const*>
|
||||
{
|
||||
static char const* TryConsumeTo(char const*&, char const* args) { return args; }
|
||||
};
|
||||
|
||||
template <typename T, size_t offset>
|
||||
struct CommandArgsConsumerNext;
|
||||
|
||||
template <typename Tuple, typename NextType, size_t offset>
|
||||
struct CommandArgsConsumerMulti
|
||||
{
|
||||
static char const* TryConsumeTo(Tuple& tuple, char const* args)
|
||||
{
|
||||
if (char const* next = CommandArgsConsumerSingle<NextType>::TryConsumeTo(std::get<offset>(tuple), args))
|
||||
return CommandArgsConsumerNext<Tuple, offset>::GoNext(tuple, next);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Tuple, typename NestedNextType, size_t offset>
|
||||
struct CommandArgsConsumerMulti<Tuple, Optional<NestedNextType>, offset>
|
||||
{
|
||||
static char const* TryConsumeTo(Tuple& tuple, char const* args)
|
||||
{
|
||||
// try with the argument
|
||||
auto& myArg = std::get<offset>(tuple);
|
||||
myArg.emplace();
|
||||
if (char const* next = CommandArgsConsumerSingle<NestedNextType>::TryConsumeTo(*(myArg.get_ptr()), args))
|
||||
if ((next = CommandArgsConsumerNext<Tuple, offset>::GoNext(tuple, next)))
|
||||
return next;
|
||||
// try again omitting the argument
|
||||
myArg = boost::none;
|
||||
if (char const* next = CommandArgsConsumerNext<Tuple, offset>::GoNext(tuple, args))
|
||||
return next;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t offset, typename... Ts>
|
||||
struct CommandArgsConsumerNext<std::tuple<Ts...>, offset>
|
||||
{
|
||||
using tuple_type = std::tuple<Ts...>;
|
||||
|
||||
template <bool C = (offset + 1 < sizeof...(Ts))>
|
||||
static std::enable_if_t<C, char const*> GoNext(tuple_type& tuple, char const* args)
|
||||
{
|
||||
return CommandArgsConsumerMulti<tuple_type, std::tuple_element_t<offset + 1, tuple_type>, offset + 1>::TryConsumeTo(tuple, args);
|
||||
}
|
||||
|
||||
template <bool C = (offset + 1 < sizeof...(Ts))>
|
||||
static std::enable_if_t<!C, char const*> GoNext(tuple_type&, char const* args)
|
||||
{
|
||||
return args;
|
||||
}
|
||||
};
|
||||
|
||||
class TC_GAME_API CommandArgs
|
||||
{
|
||||
public:
|
||||
CommandArgs(char const* args) : _original(args), _args(args) {}
|
||||
|
||||
template <typename T1, typename T2, typename... Ts>
|
||||
auto TryConsume()
|
||||
{
|
||||
Optional<std::tuple<advstd::remove_cvref_t<T1>, advstd::remove_cvref_t<T2>, advstd::remove_cvref_t<Ts>...>> rv;
|
||||
rv.emplace();
|
||||
if (!TryConsumeToTuple<0>(*(rv.get_ptr())))
|
||||
rv = boost::none;
|
||||
return rv;
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
auto TryConsume()
|
||||
{
|
||||
using T = advstd::remove_cvref_t<T1>;
|
||||
Optional<T> rv;
|
||||
rv.emplace();
|
||||
if (char const* next = CommandArgsConsumerSingle<T>::TryConsumeTo(*(rv.get_ptr()), _args))
|
||||
_args = next;
|
||||
else
|
||||
rv = boost::none;
|
||||
return rv;
|
||||
}
|
||||
|
||||
template <size_t offset = 0, typename T>
|
||||
bool TryConsumeToTuple(T& tuple)
|
||||
{
|
||||
if (char const* next = CommandArgsConsumerMulti<T, std::tuple_element_t<offset, T>, offset>::TryConsumeTo(tuple, _args))
|
||||
{
|
||||
_args = next;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Reset() { _args = _original; }
|
||||
|
||||
char const* GetFullArgs() const { return _original; }
|
||||
char const* GetRemainingArgs() const { return _args; }
|
||||
|
||||
bool IsEmpty() const { return !!*_args; }
|
||||
explicit operator bool() const { return IsEmpty(); }
|
||||
|
||||
private:
|
||||
char const* const _original;
|
||||
char const* _args;
|
||||
};
|
||||
|
||||
template <typename T> struct ChatCommandHandlerToTuple { static_assert(!advstd::is_same_v<T,T>, "Invalid command handler signature"); };
|
||||
template <typename... Ts> struct ChatCommandHandlerToTuple<bool(*)(ChatHandler*, Ts...)> { using type = std::tuple<ChatHandler*, advstd::remove_cvref_t<Ts>...>; };
|
||||
|
||||
template <typename T> struct ChatCommandStoreLastArg { static void store(T&, CommandArgs&) {} };
|
||||
template <> struct ChatCommandStoreLastArg<char const*> { static void store(char const*& arg, CommandArgs& args) { arg = args.GetRemainingArgs(); } };
|
||||
template <> struct ChatCommandStoreLastArg<CommandArgs*> { static void store(CommandArgs*& arg, CommandArgs& args) { arg = &args; } };
|
||||
|
||||
class TC_GAME_API ChatCommand
|
||||
{
|
||||
using wrapper_func = bool(void*, ChatHandler*, char const*);
|
||||
|
||||
public:
|
||||
template <typename TypedHandler>
|
||||
ChatCommand(char const* name, uint32 permission, bool allowConsole, TypedHandler handler, std::string help)
|
||||
: Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Help(std::move(help)), ChildCommands({})
|
||||
{
|
||||
_wrapper = [](void* handler, ChatHandler* chatHandler, char const* argsStr)
|
||||
{
|
||||
using tuple_type = typename ChatCommandHandlerToTuple<TypedHandler>::type;
|
||||
|
||||
tuple_type arguments;
|
||||
std::get<0>(arguments) = chatHandler;
|
||||
|
||||
CommandArgs args(argsStr);
|
||||
if (args.TryConsumeToTuple<1>(arguments))
|
||||
{
|
||||
auto& last = std::get<advstd::tuple_size_v<tuple_type>-1>(arguments);
|
||||
ChatCommandStoreLastArg<advstd::remove_cvref_t<decltype(last)>>::store(last, args);
|
||||
return advstd::apply(reinterpret_cast<TypedHandler>(handler), std::move(arguments));
|
||||
}
|
||||
else
|
||||
return false;
|
||||
};
|
||||
_handler = reinterpret_cast<void*>(handler);
|
||||
}
|
||||
|
||||
ChatCommand(char const* name, uint32 permission, bool allowConsole, std::nullptr_t, std::string help, std::vector<ChatCommand> childCommands = {})
|
||||
: Name(ASSERT_NOTNULL(name)), Permission(permission), AllowConsole(allowConsole), Help(std::move(help)), ChildCommands(std::move(childCommands))
|
||||
{
|
||||
_wrapper = nullptr;
|
||||
_handler = nullptr;
|
||||
}
|
||||
|
||||
bool operator()(ChatHandler* chatHandler, char const* args) const
|
||||
{
|
||||
ASSERT(_wrapper && _handler);
|
||||
return _wrapper(_handler, chatHandler, args);
|
||||
}
|
||||
|
||||
bool HasHandler() const { return !!_handler; }
|
||||
|
||||
char const* Name;
|
||||
uint32 Permission; // function pointer required correct align (use uint32)
|
||||
bool AllowConsole;
|
||||
std::string Help;
|
||||
std::vector<ChatCommand> ChildCommands;
|
||||
|
||||
private:
|
||||
wrapper_func* _wrapper;
|
||||
void* _handler;
|
||||
};
|
||||
|
||||
#endif
|
||||
37
src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
Normal file
37
src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ChatCommandArgs.h"
|
||||
#include "ChatCommand.h"
|
||||
#include "ChatCommandHyperlinks.h"
|
||||
#include "ObjectMgr.h"
|
||||
|
||||
using namespace Trinity::ChatCommands;
|
||||
|
||||
struct GameTeleVisitor
|
||||
{
|
||||
using value_type = GameTele const*;
|
||||
value_type operator()(Hyperlink<tele> tele) const { return sObjectMgr->GetGameTele(tele); }
|
||||
value_type operator()(std::string const& tele) const { return sObjectMgr->GetGameTele(tele); }
|
||||
};
|
||||
char const* Trinity::ChatCommands::ArgInfo<GameTele const*>::TryConsume(GameTele const*& data, char const* args)
|
||||
{
|
||||
Variant<Hyperlink<tele>, std::string> val;
|
||||
if ((args = CommandArgsConsumerSingle<decltype(val)>::TryConsumeTo(val, args)))
|
||||
data = boost::apply_visitor(GameTeleVisitor(), val);
|
||||
return args;
|
||||
}
|
||||
120
src/server/game/Chat/ChatCommands/ChatCommandArgs.h
Normal file
120
src/server/game/Chat/ChatCommands/ChatCommandArgs.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_CHATCOMMANDARGS_H
|
||||
#define TRINITY_CHATCOMMANDARGS_H
|
||||
|
||||
#include "ChatCommandHelpers.h"
|
||||
#include "ChatCommandTags.h"
|
||||
#include "ChatCommandHyperlinks.h"
|
||||
|
||||
struct GameTele;
|
||||
|
||||
namespace Trinity {
|
||||
namespace ChatCommands {
|
||||
|
||||
/************************** ARGUMENT HANDLERS *******************************************\
|
||||
|* Define how to extract contents of a certain requested type from a string *|
|
||||
|* Must implement the following: *|
|
||||
|* - TryConsume: T&, char const* -> char const* *|
|
||||
|* returns nullptr if no match, otherwise pointer to first character of next token *|
|
||||
|* - if nullptr is returned, state of T& is indeterminate *|
|
||||
|* - otherwise, T& should be initialized to the intended return value *|
|
||||
|* *|
|
||||
\****************************************************************************************/
|
||||
template <typename T, typename = void>
|
||||
struct ArgInfo { static_assert(!advstd::is_same_v<T,T>, "Invalid command parameter type - see ChatCommandArgs.h for possible types"); };
|
||||
|
||||
// catch-all for signed integral types
|
||||
template <typename T>
|
||||
struct ArgInfo<T, std::enable_if_t<advstd::is_integral_v<T> && advstd::is_signed_v<T>>>
|
||||
{
|
||||
static char const* TryConsume(T& val, char const* args)
|
||||
{
|
||||
char const* next = args;
|
||||
std::string token(args, tokenize(next));
|
||||
try { val = std::stoll(token); }
|
||||
catch (...) { return nullptr; }
|
||||
return next;
|
||||
}
|
||||
};
|
||||
|
||||
// catch-all for unsigned integral types
|
||||
template <typename T>
|
||||
struct ArgInfo<T, std::enable_if_t<advstd::is_integral_v<T> && advstd::is_unsigned_v<T>>>
|
||||
{
|
||||
static char const* TryConsume(T& val, char const* args)
|
||||
{
|
||||
char const* next = args;
|
||||
std::string token(args, tokenize(next));
|
||||
try { val = std::stoull(token); }
|
||||
catch (...) { return nullptr; }
|
||||
return next;
|
||||
}
|
||||
};
|
||||
|
||||
// catch-all for floating point types
|
||||
template <typename T>
|
||||
struct ArgInfo<T, std::enable_if_t<advstd::is_floating_point_v<T>>>
|
||||
{
|
||||
static char const* TryConsume(T& val, char const* args)
|
||||
{
|
||||
char const* next = args;
|
||||
std::string token(args, tokenize(next));
|
||||
try { val = std::stold(token); }
|
||||
catch (...) { return nullptr; }
|
||||
return std::isfinite(val) ? next : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// string
|
||||
template <>
|
||||
struct ArgInfo<std::string, void>
|
||||
{
|
||||
static char const* TryConsume(std::string& val, char const* args)
|
||||
{
|
||||
char const* next = args;
|
||||
if (size_t len = tokenize(next))
|
||||
{
|
||||
val.assign(args, len);
|
||||
return next;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
// a container tag
|
||||
template <typename T>
|
||||
struct ArgInfo<T, std::enable_if_t<advstd::is_base_of_v<ContainerTag, T>>>
|
||||
{
|
||||
static char const* TryConsume(T& tag, char const* args)
|
||||
{
|
||||
return tag.TryConsume(args);
|
||||
}
|
||||
};
|
||||
|
||||
// GameTele* from string name or link
|
||||
template <>
|
||||
struct TC_GAME_API ArgInfo<GameTele const*>
|
||||
{
|
||||
static char const* TryConsume(GameTele const*&, char const*);
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
75
src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
Normal file
75
src/server/game/Chat/ChatCommands/ChatCommandHelpers.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_CHATCOMMANDHELPERS_H
|
||||
#define TRINITY_CHATCOMMANDHELPERS_H
|
||||
|
||||
#include "advstd.h"
|
||||
#include <type_traits>
|
||||
|
||||
namespace Trinity {
|
||||
namespace ChatCommands {
|
||||
|
||||
static const char COMMAND_DELIMITER = ' ';
|
||||
|
||||
/***************** HELPERS *************************\
|
||||
|* These really aren't for outside use... *|
|
||||
\***************************************************/
|
||||
inline size_t tokenize(char const*& end)
|
||||
{
|
||||
size_t len = 0;
|
||||
for (; *end && *end != COMMAND_DELIMITER; ++end, ++len);
|
||||
for (; *end && *end == COMMAND_DELIMITER; ++end);
|
||||
return len;
|
||||
}
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct tag_base
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using tag_base_t = typename tag_base<T>::type;
|
||||
|
||||
template <typename...>
|
||||
struct are_all_assignable
|
||||
{
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
template <typename T1, typename T2, typename... Ts>
|
||||
struct are_all_assignable<T1, T2, Ts...>
|
||||
{
|
||||
static constexpr bool value = advstd::is_assignable_v<T1&, T2> && are_all_assignable<T1, Ts...>::value;
|
||||
};
|
||||
|
||||
template <size_t index, typename T1, typename... Ts>
|
||||
struct get_nth : get_nth<index-1, Ts...> { };
|
||||
|
||||
template <typename T1, typename... Ts>
|
||||
struct get_nth<0, T1, Ts...>
|
||||
{
|
||||
using type = T1;
|
||||
};
|
||||
|
||||
template <size_t index, typename... Ts>
|
||||
using get_nth_t = typename get_nth<index, Ts...>::type;
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
132
src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h
Normal file
132
src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_CHATCOMMANDHYPERLINKS_H
|
||||
#define TRINITY_CHATCOMMANDHYPERLINKS_H
|
||||
|
||||
#include "ChatCommandHelpers.h"
|
||||
#include "ChatCommandTags.h"
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
namespace Trinity {
|
||||
namespace ChatCommands {
|
||||
|
||||
// for details on what ContainerTag has to expose, see ChatCommandTags.h
|
||||
template <typename linktag>
|
||||
struct Hyperlink : public ContainerTag
|
||||
{
|
||||
typedef typename linktag::value_type value_type;
|
||||
typedef advstd::remove_cvref_t<value_type> storage_type;
|
||||
|
||||
public:
|
||||
operator value_type() const { return val; }
|
||||
|
||||
char const* TryConsume(char const* pos)
|
||||
{
|
||||
//color tag
|
||||
if (*(pos++) != '|' || *(pos++) != 'c')
|
||||
return nullptr;
|
||||
for (uint8 i = 0; i < 8; ++i)
|
||||
if (!*(pos++)) // make sure we don't overrun a terminator
|
||||
return nullptr;
|
||||
// link data start tag
|
||||
if (*(pos++) != '|' || *(pos++) != 'H')
|
||||
return nullptr;
|
||||
// link tag, should match argument
|
||||
char const* tag = linktag::tag();
|
||||
while (*tag)
|
||||
if (*(pos++) != *(tag++))
|
||||
return nullptr;
|
||||
// separator
|
||||
if (*(pos++) != ':')
|
||||
return nullptr;
|
||||
// ok, link data, let's figure out how long it is
|
||||
char const* datastart = pos;
|
||||
size_t datalength = 0;
|
||||
while (*pos && *(pos++) != '|')
|
||||
++datalength;
|
||||
// ok, next should be link data end tag...
|
||||
if (*(pos++) != 'h')
|
||||
return nullptr;
|
||||
// then visible link text, skip to next '|', should be '|h|r' terminator
|
||||
while (*pos && *(pos++) != '|');
|
||||
if (*(pos++) != 'h' || *(pos++) != '|' || *(pos++) != 'r')
|
||||
return nullptr;
|
||||
// finally, skip to end of token
|
||||
tokenize(pos);
|
||||
// store value
|
||||
if (!linktag::StoreTo(val, datastart, datalength))
|
||||
return nullptr;
|
||||
|
||||
// return final pos
|
||||
return pos;
|
||||
}
|
||||
|
||||
private:
|
||||
storage_type val;
|
||||
};
|
||||
|
||||
/************************** LINK TAGS ***************************************************\
|
||||
|* Link tags must abide by the following: *|
|
||||
|* - MUST expose ::value_type typedef *|
|
||||
|* - storage type is remove_cvref_t<value_type> *|
|
||||
|* - MUST expose static ::tag method, void -> const char* *|
|
||||
|* - this method SHOULD be constexpr *|
|
||||
|* - returns identifier string for the link ("creature", "creature_entry", "item") *|
|
||||
|* - MUST expose static ::StoreTo method, (storage&, char const*, size_t) *|
|
||||
|* - assign value_type& based on content of std::string(char const*, size_t) *|
|
||||
|* - return value indicates success/failure *|
|
||||
|* - for integral/string types this can be achieved by extending base_tag *|
|
||||
\****************************************************************************************/
|
||||
struct base_tag
|
||||
{
|
||||
static bool StoreTo(std::string& val, char const* pos, size_t len)
|
||||
{
|
||||
val.assign(pos, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<advstd::is_integral_v<T> && advstd::is_unsigned_v<T>, bool> StoreTo(T& val, char const* pos, size_t len)
|
||||
{
|
||||
try { val = std::stoull(std::string(pos, len)); }
|
||||
catch (...) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::enable_if_t<advstd::is_integral_v<T> && advstd::is_signed_v<T>, bool> StoreTo(T& val, char const* pos, size_t len)
|
||||
{
|
||||
try { val = std::stoll(std::string(pos, len)); }
|
||||
catch (...) { return false; }
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#define make_base_tag(ltag, type) struct ltag : public base_tag { typedef type value_type; static constexpr char const* tag() { return #ltag; } }
|
||||
make_base_tag(areatrigger, uint32);
|
||||
make_base_tag(creature, ObjectGuid::LowType);
|
||||
make_base_tag(creature_entry, uint32);
|
||||
make_base_tag(gameobject, ObjectGuid::LowType);
|
||||
make_base_tag(taxinode, uint32);
|
||||
make_base_tag(tele, uint32);
|
||||
#undef make_base_tag
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
|
||||
123
src/server/game/Chat/ChatCommands/ChatCommandTags.h
Normal file
123
src/server/game/Chat/ChatCommands/ChatCommandTags.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 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 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITY_CHATCOMMANDTAGS_H
|
||||
#define TRINITY_CHATCOMMANDTAGS_H
|
||||
|
||||
#include "ChatCommandHelpers.h"
|
||||
#include "Optional.h"
|
||||
#include <boost/variant.hpp>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace Trinity {
|
||||
namespace ChatCommands {
|
||||
/************************** CONTAINER TAGS **********************************************\
|
||||
|* Simple holder classes to differentiate between extraction methods *|
|
||||
|* Should inherit from ContainerTag for template identification *|
|
||||
|* Must implement the following: *|
|
||||
|* - TryConsume: char const* -> char const* *|
|
||||
|* returns nullptr if no match, otherwise pointer to first character of next token *|
|
||||
|* - typedef value_type of type that is contained within the tag *|
|
||||
|* - cast operator to value_type *|
|
||||
|* *|
|
||||
\****************************************************************************************/
|
||||
struct ContainerTag {};
|
||||
template <typename T>
|
||||
struct tag_base<T, std::enable_if_t<advstd::is_base_of_v<ContainerTag, T>>>
|
||||
{
|
||||
using type = typename T::value_type;
|
||||
};
|
||||
|
||||
template <char c1, char... chars>
|
||||
struct ExactSequence : public ContainerTag
|
||||
{
|
||||
using value_type = void;
|
||||
|
||||
static constexpr bool isSingleChar = !sizeof...(chars);
|
||||
|
||||
template <bool C = isSingleChar>
|
||||
static typename std::enable_if_t<!C, char const*> _TryConsume(char const* pos)
|
||||
{
|
||||
if (*(pos++) == c1)
|
||||
return ExactSequence<chars...>::_TryConsume(pos);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <bool C = isSingleChar>
|
||||
static typename std::enable_if_t<C, char const*> _TryConsume(char const* pos)
|
||||
{
|
||||
if (*(pos++) != c1)
|
||||
return nullptr;
|
||||
// if more of string is left, tokenize should return 0 (otherwise we didn't reach end of token yet)
|
||||
return *pos && tokenize(pos) ? nullptr : pos;
|
||||
}
|
||||
|
||||
char const* TryConsume(char const* pos) const { return ExactSequence::_TryConsume(pos); }
|
||||
};
|
||||
|
||||
/************************** VARIANT TAG LOGIC *********************************\
|
||||
|* This has some special handling over in ChatCommand.h *|
|
||||
\******************************************************************************/
|
||||
|
||||
template <typename T>
|
||||
struct CastToVisitor {
|
||||
using result_type = T;
|
||||
|
||||
template <typename U>
|
||||
T operator()(U const& v) const { return v; }
|
||||
};
|
||||
|
||||
template <typename T1, typename... Ts>
|
||||
struct Variant : public boost::variant<T1, Ts...>
|
||||
{
|
||||
using first_type = tag_base_t<T1>;
|
||||
static constexpr bool have_operators = are_all_assignable<first_type, tag_base_t<Ts>...>::value;
|
||||
|
||||
template <bool C = have_operators>
|
||||
operator std::enable_if_t<C, first_type>()
|
||||
{
|
||||
return operator*();
|
||||
}
|
||||
|
||||
template <bool C = have_operators>
|
||||
std::enable_if_t<C, first_type> operator*()
|
||||
{
|
||||
return boost::apply_visitor(CastToVisitor<first_type>(), *this);
|
||||
}
|
||||
|
||||
template <bool C = have_operators>
|
||||
std::enable_if_t<C, first_type const&> operator*() const
|
||||
{
|
||||
return boost::apply_visitor(CastToVisitor<first_type const&>(), *this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Variant& operator=(T&& arg) { boost::variant<T1, Ts...>::operator=(std::forward<T>(arg)); return *this; }
|
||||
|
||||
template <size_t index>
|
||||
decltype(auto) get() const { return boost::get<get_nth_t<index, T1, Ts...>>(*this); }
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
@@ -34,9 +34,11 @@ EndScriptData */
|
||||
#include "RBAC.h"
|
||||
#include "SupportMgr.h"
|
||||
#include "Transport.h"
|
||||
#include "Util.h"
|
||||
#include "WorldSession.h"
|
||||
#include <sstream>
|
||||
|
||||
using namespace Trinity::ChatCommands;
|
||||
class go_commandscript : public CommandScript
|
||||
{
|
||||
public:
|
||||
@@ -44,15 +46,27 @@ public:
|
||||
|
||||
std::vector<ChatCommand> GetCommands() const override
|
||||
{
|
||||
static std::vector<ChatCommand> goCreatureCommandTable =
|
||||
{
|
||||
{ "id", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoCreatureCIdCommand, "" },
|
||||
{ "", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoCreatureSpawnIdCommand, "" }
|
||||
};
|
||||
|
||||
static std::vector<ChatCommand> goGameObjectCommandTable =
|
||||
{
|
||||
{ "id", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoGameObjectGOIdCommand, "" },
|
||||
{ "", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoGameObjectSpawnIdCommand, "" }
|
||||
};
|
||||
|
||||
static std::vector<ChatCommand> goCommandTable =
|
||||
{
|
||||
{ "creature", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoCreatureCommand, "" },
|
||||
{ "creature", rbac::RBAC_PERM_COMMAND_GO, false, nullptr, "", goCreatureCommandTable },
|
||||
{ "gameobject", rbac::RBAC_PERM_COMMAND_GO, false, nullptr, "", goGameObjectCommandTable },
|
||||
{ "graveyard", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoGraveyardCommand, "" },
|
||||
{ "grid", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoGridCommand, "" },
|
||||
{ "object", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoObjectCommand, "" },
|
||||
{ "quest", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoQuestCommand, "" },
|
||||
{ "taxinode", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTaxinodeCommand, "" },
|
||||
{ "trigger", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTriggerCommand, "" },
|
||||
{ "areatrigger", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoAreaTriggerCommand, "" },
|
||||
{ "zonexy", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoZoneXYCommand, "" },
|
||||
{ "xyz", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoXYZCommand, "" },
|
||||
{ "bugticket", rbac::RBAC_PERM_COMMAND_GO, false, &HandleGoTicketCommand<BugTicket>, "" },
|
||||
@@ -69,84 +83,13 @@ public:
|
||||
return commandTable;
|
||||
}
|
||||
|
||||
/** \brief Teleport the GM to the specified creature
|
||||
*
|
||||
* .gocreature <GUID> --> TP using creature.guid
|
||||
* .gocreature azuregos --> TP player to the mob with this name
|
||||
* Warning: If there is more than one mob with this name
|
||||
* you will be teleported to the first one that is found.
|
||||
* .gocreature id 6109 --> TP player to the mob, that has this creature_template.entry
|
||||
* Warning: If there is more than one mob with this "id"
|
||||
* you will be teleported to the first one that is found.
|
||||
*/
|
||||
//teleport to creature
|
||||
static bool HandleGoCreatureCommand(ChatHandler* handler, char const* args)
|
||||
static bool DoTeleport(ChatHandler* handler, WorldLocation loc)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
// "id" or number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
|
||||
char* param1 = handler->extractKeyFromLink((char*)args, "Hcreature");
|
||||
if (!param1)
|
||||
return false;
|
||||
|
||||
std::ostringstream whereClause;
|
||||
|
||||
// User wants to teleport to the NPC's template entry
|
||||
if (strcmp(param1, "id") == 0)
|
||||
if (!MapManager::IsValidMapCoord(loc) || sObjectMgr->IsTransportMap(loc.GetMapId()))
|
||||
{
|
||||
// Get the "creature_template.entry"
|
||||
// number or [name] Shift-click form |color|Hcreature_entry:creature_id|h[name]|h|r
|
||||
char* tail = strtok(nullptr, "");
|
||||
if (!tail)
|
||||
return false;
|
||||
char* id = handler->extractKeyFromLink(tail, "Hcreature_entry");
|
||||
if (!id)
|
||||
return false;
|
||||
|
||||
uint32 entry = atoul(id);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
whereClause << "WHERE id = '" << entry << '\'';
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectGuid::LowType guidLow = atoull(param1);
|
||||
|
||||
// Number is invalid - maybe the user specified the mob's name
|
||||
if (!guidLow)
|
||||
{
|
||||
std::string name = param1;
|
||||
WorldDatabase.EscapeString(name);
|
||||
whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name LIKE '" << name << '\'';
|
||||
}
|
||||
else
|
||||
whereClause << "WHERE guid = '" << guidLow << '\'';
|
||||
}
|
||||
|
||||
QueryResult result = WorldDatabase.PQuery("SELECT position_x, position_y, position_z, orientation, map FROM creature %s", whereClause.str().c_str());
|
||||
if (!result)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
if (result->GetRowCount() > 1)
|
||||
handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
float x = fields[0].GetFloat();
|
||||
float y = fields[1].GetFloat();
|
||||
float z = fields[2].GetFloat();
|
||||
float o = fields[3].GetFloat();
|
||||
uint32 mapId = fields[4].GetUInt16();
|
||||
|
||||
if (!MapManager::IsValidMapCoord(mapId, x, y, z, o) || sObjectMgr->IsTransportMap(mapId))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
|
||||
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ());
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
@@ -157,30 +100,96 @@ public:
|
||||
else
|
||||
player->SaveRecallPosition(); // save only in non-flight case
|
||||
|
||||
player->TeleportTo(mapId, x, y, z, o);
|
||||
player->TeleportTo(loc);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleGoGraveyardCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleGoCreatureSpawnIdCommand(ChatHandler* handler, Variant<Hyperlink<creature>, ObjectGuid::LowType> spawnId)
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
if (!*args)
|
||||
CreatureData const* spawnpoint = sObjectMgr->GetCreatureData(spawnId);
|
||||
if (!spawnpoint)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
char* gyId = strtok((char*)args, " ");
|
||||
if (!gyId)
|
||||
return DoTeleport(handler, spawnpoint->spawnPoint);
|
||||
}
|
||||
|
||||
static bool HandleGoCreatureCIdCommand(ChatHandler* handler, Variant<Hyperlink<creature_entry>, uint32> cId)
|
||||
{
|
||||
CreatureData const* spawnpoint = nullptr;
|
||||
for (auto const& pair : sObjectMgr->GetAllCreatureData())
|
||||
{
|
||||
if (pair.second.id != *cId)
|
||||
continue;
|
||||
|
||||
if (!spawnpoint)
|
||||
spawnpoint = &pair.second;
|
||||
else
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!spawnpoint)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOCREATNOTFOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 graveyardId = atoul(gyId);
|
||||
return DoTeleport(handler, spawnpoint->spawnPoint);
|
||||
}
|
||||
|
||||
if (!graveyardId)
|
||||
static bool HandleGoGameObjectSpawnIdCommand(ChatHandler* handler, uint32 spawnId)
|
||||
{
|
||||
GameObjectData const* spawnpoint = sObjectMgr->GetGameObjectData(spawnId);
|
||||
if (!spawnpoint)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldSafeLocsEntry const* gy = sObjectMgr->GetWorldSafeLoc(graveyardId);
|
||||
return DoTeleport(handler, spawnpoint->spawnPoint);
|
||||
}
|
||||
|
||||
static bool HandleGoGameObjectGOIdCommand(ChatHandler* handler, uint32 goId)
|
||||
{
|
||||
GameObjectData const* spawnpoint = nullptr;
|
||||
for (auto const& pair : sObjectMgr->GetAllGameObjectData())
|
||||
{
|
||||
if (pair.second.id != goId)
|
||||
continue;
|
||||
|
||||
if (!spawnpoint)
|
||||
spawnpoint = &pair.second;
|
||||
else
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOCREATMULTIPLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!spawnpoint)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoTeleport(handler, spawnpoint->spawnPoint);
|
||||
}
|
||||
|
||||
static bool HandleGoGraveyardCommand(ChatHandler* handler, uint32 gyId)
|
||||
{
|
||||
WorldSafeLocsEntry const* gy = sObjectMgr->GetWorldSafeLoc(gyId);
|
||||
if (!gy)
|
||||
{
|
||||
handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, graveyardId);
|
||||
handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDNOEXIST, gyId);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
@@ -192,6 +201,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
// stop flight if need
|
||||
if (player->IsInFlight())
|
||||
player->FinishTaxiFlight();
|
||||
@@ -203,25 +213,14 @@ public:
|
||||
}
|
||||
|
||||
//teleport to grid
|
||||
static bool HandleGoGridCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleGoGridCommand(ChatHandler* handler, float gridX, float gridY, Optional<uint32> oMapId)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
char* gridX = strtok((char*)args, " ");
|
||||
char* gridY = strtok(nullptr, " ");
|
||||
char* id = strtok(nullptr, " ");
|
||||
|
||||
if (!gridX || !gridY)
|
||||
return false;
|
||||
|
||||
uint32 mapId = id ? atoul(id) : player->GetMapId();
|
||||
uint32 mapId = oMapId.get_value_or(player->GetMapId());
|
||||
|
||||
// center of grid
|
||||
float x = ((float)atof(gridX) - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
|
||||
float y = ((float)atof(gridY) - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
|
||||
float x = (gridX - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
|
||||
float y = (gridY - CENTER_GRID_ID + 0.5f) * SIZE_OF_GRIDS;
|
||||
|
||||
if (!MapManager::IsValidMapCoord(mapId, x, y))
|
||||
{
|
||||
@@ -243,49 +242,6 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
//teleport to gameobject
|
||||
static bool HandleGoObjectCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
// number or [name] Shift-click form |color|Hgameobject:go_guid|h[name]|h|r
|
||||
char* id = handler->extractKeyFromLink((char*)args, "Hgameobject");
|
||||
if (!id)
|
||||
return false;
|
||||
|
||||
ObjectGuid::LowType guidLow = atoull(id);
|
||||
if (!guidLow)
|
||||
return false;
|
||||
|
||||
// by DB guid
|
||||
GameObjectData const* goData = sObjectMgr->GetGameObjectData(guidLow);
|
||||
if (!goData)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_GOOBJNOTFOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MapManager::IsValidMapCoord(goData->spawnPoint) || sObjectMgr->IsTransportMap(goData->spawnPoint.GetMapId()))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, goData->spawnPoint.GetPositionX(), goData->spawnPoint.GetPositionY(), goData->spawnPoint.GetMapId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// stop flight if need
|
||||
if (player->IsInFlight())
|
||||
player->FinishTaxiFlight();
|
||||
else
|
||||
player->SaveRecallPosition(); // save only in non-flight case
|
||||
|
||||
player->TeleportTo(goData->spawnPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleGoQuestCommand(ChatHandler* handler, char const* args)
|
||||
{
|
||||
if (!*args)
|
||||
@@ -348,21 +304,8 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleGoTaxinodeCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleGoTaxinodeCommand(ChatHandler* handler, Variant<Hyperlink<taxinode>, uint32> nodeId)
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
char* id = handler->extractKeyFromLink((char*)args, "Htaxinode");
|
||||
if (!id)
|
||||
return false;
|
||||
|
||||
uint32 nodeId = atoul(id);
|
||||
if (!nodeId)
|
||||
return false;
|
||||
|
||||
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(nodeId);
|
||||
if (!node)
|
||||
{
|
||||
@@ -370,41 +313,11 @@ public:
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((node->Pos.X == 0.0f && node->Pos.Y == 0.0f && node->Pos.Z == 0.0f) ||
|
||||
!MapManager::IsValidMapCoord(node->ContinentID, node->Pos.X, node->Pos.Y, node->Pos.Z))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, node->Pos.X, node->Pos.Y, uint32(node->ContinentID));
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// stop flight if need
|
||||
if (player->IsInFlight())
|
||||
player->FinishTaxiFlight();
|
||||
else
|
||||
player->SaveRecallPosition(); // save only in non-flight case
|
||||
|
||||
player->TeleportTo(node->ContinentID, node->Pos.X, node->Pos.Y, node->Pos.Z, player->GetOrientation());
|
||||
return true;
|
||||
return DoTeleport(handler, { node->ContinentID, { node->Pos.X, node->Pos.Y, node->Pos.Z } });
|
||||
}
|
||||
|
||||
static bool HandleGoTriggerCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleGoAreaTriggerCommand(ChatHandler* handler, Variant<Hyperlink<areatrigger>, uint32> areaTriggerId)
|
||||
{
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
char* id = strtok((char*)args, " ");
|
||||
if (!id)
|
||||
return false;
|
||||
|
||||
uint32 areaTriggerId = atoul(id);
|
||||
|
||||
if (!areaTriggerId)
|
||||
return false;
|
||||
|
||||
AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(areaTriggerId);
|
||||
if (!at)
|
||||
{
|
||||
@@ -412,22 +325,7 @@ public:
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MapManager::IsValidMapCoord(at->ContinentID, at->Pos.X, at->Pos.Y, at->Pos.Z))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, at->Pos.X, at->Pos.Y, uint32(at->ContinentID));
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// stop flight if need
|
||||
if (player->IsInFlight())
|
||||
player->FinishTaxiFlight();
|
||||
else
|
||||
player->SaveRecallPosition(); // save only in non-flight case
|
||||
|
||||
player->TeleportTo(at->ContinentID, at->Pos.X, at->Pos.Y, at->Pos.Z, player->GetOrientation());
|
||||
return true;
|
||||
return DoTeleport(handler, { uint32(at->ContinentID), { at->Pos.X, at->Pos.Y, at->Pos.Z } });
|
||||
}
|
||||
|
||||
//teleport at coordinates
|
||||
@@ -503,32 +401,13 @@ public:
|
||||
}
|
||||
|
||||
//teleport at coordinates, including Z and orientation
|
||||
static bool HandleGoXYZCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleGoXYZCommand(ChatHandler* handler, float x, float y, Optional<float> z, Optional<uint32> id, Optional<float> o)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
char* goX = strtok((char*)args, " ");
|
||||
char* goY = strtok(nullptr, " ");
|
||||
char* goZ = strtok(nullptr, " ");
|
||||
char* id = strtok(nullptr, " ");
|
||||
char* port = strtok(nullptr, " ");
|
||||
|
||||
if (!goX || !goY)
|
||||
return false;
|
||||
|
||||
float x = (float)atof(goX);
|
||||
float y = (float)atof(goY);
|
||||
float z;
|
||||
float ort = port ? (float)atof(port) : player->GetOrientation();
|
||||
uint32 mapId = id ? atoul(id) : player->GetMapId();
|
||||
|
||||
if (goZ)
|
||||
uint32 mapId = id.get_value_or(player->GetMapId());
|
||||
if (z)
|
||||
{
|
||||
z = (float)atof(goZ);
|
||||
if (!MapManager::IsValidMapCoord(mapId, x, y, z))
|
||||
if (!MapManager::IsValidMapCoord(mapId, x, y, *z))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, mapId);
|
||||
handler->SetSentErrorMessage(true);
|
||||
@@ -547,30 +426,12 @@ public:
|
||||
z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y));
|
||||
}
|
||||
|
||||
// stop flight if need
|
||||
if (player->IsInFlight())
|
||||
player->FinishTaxiFlight();
|
||||
else
|
||||
player->SaveRecallPosition(); // save only in non-flight case
|
||||
|
||||
player->TeleportTo(mapId, x, y, z, ort);
|
||||
return true;
|
||||
return DoTeleport(handler, { mapId, { x, y, *z, o.get_value_or(0.0f) } });
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool HandleGoTicketCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleGoTicketCommand(ChatHandler* handler, uint32 ticketId)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
char* id = strtok((char*)args, " ");
|
||||
if (!id)
|
||||
return false;
|
||||
|
||||
uint32 ticketId = atoul(id);
|
||||
if (!ticketId)
|
||||
return false;
|
||||
|
||||
T* ticket = sSupportMgr->GetTicket<T>(ticketId);
|
||||
if (!ticket)
|
||||
{
|
||||
@@ -590,95 +451,47 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleGoOffsetCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleGoOffsetCommand(ChatHandler* handler, float dX, Optional<float> dY, Optional<float> dZ, Optional<float> dO)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
WorldLocation loc = handler->GetSession()->GetPlayer()->GetWorldLocation();
|
||||
loc.RelocateOffset({ dX, dY.get_value_or(0.0f), dZ.get_value_or(0.0f), dO.get_value_or(0.0f) });
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
char* goX = strtok((char*)args, " ");
|
||||
char* goY = strtok(nullptr, " ");
|
||||
char* goZ = strtok(nullptr, " ");
|
||||
char* port = strtok(nullptr, " ");
|
||||
|
||||
float x, y, z, o;
|
||||
player->GetPosition(x, y, z, o);
|
||||
if (goX)
|
||||
x += atof(goX);
|
||||
if (goY)
|
||||
y += atof(goY);
|
||||
if (goZ)
|
||||
z += atof(goZ);
|
||||
if (port)
|
||||
o += atof(port);
|
||||
|
||||
if (!Trinity::IsValidMapCoord(x, y, z, o))
|
||||
{
|
||||
handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, player->GetMapId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// stop flight if need
|
||||
if (player->IsInFlight())
|
||||
player->FinishTaxiFlight();
|
||||
else
|
||||
player->SaveRecallPosition(); // save only in non-flight case
|
||||
|
||||
player->TeleportTo(player->GetMapId(), x, y, z, o);
|
||||
return true;
|
||||
return DoTeleport(handler, loc);
|
||||
}
|
||||
|
||||
static bool HandleGoInstanceCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleGoInstanceCommand(ChatHandler* handler, std::vector<std::string> const& labels)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
char* pos = const_cast<char*>(args);
|
||||
do *pos = tolower(*pos);
|
||||
while (*(++pos));
|
||||
|
||||
Tokenizer labels(args, ' ');
|
||||
uint32 mapid = 0;
|
||||
if (labels.size() == 1)
|
||||
|
||||
std::multimap<uint32, std::pair<uint32, std::string>> matches;
|
||||
for (auto const& pair : sObjectMgr->GetInstanceTemplates())
|
||||
{
|
||||
try { mapid = std::stoi(labels[0]); }
|
||||
catch (...) {}
|
||||
uint32 count = 0;
|
||||
std::string const& scriptName = sObjectMgr->GetScriptName(pair.second.ScriptId);
|
||||
for (auto const& label : labels)
|
||||
if (StringContainsStringI(scriptName, label))
|
||||
++count;
|
||||
|
||||
if (count)
|
||||
matches.emplace(count, decltype(matches)::mapped_type({ pair.first, scriptName }));
|
||||
}
|
||||
|
||||
if (!mapid)
|
||||
if (matches.empty())
|
||||
{
|
||||
std::multimap<uint32, std::pair<uint32, std::string>> matches;
|
||||
for (auto const& pair : sObjectMgr->GetInstanceTemplates())
|
||||
{
|
||||
uint32 count = 0;
|
||||
std::string const& scriptName = sObjectMgr->GetScriptName(pair.second.ScriptId);
|
||||
for (char const* label : labels)
|
||||
if (scriptName.find(label) != std::string::npos)
|
||||
++count;
|
||||
|
||||
if (count)
|
||||
matches.emplace(count, decltype(matches)::mapped_type({ pair.first, scriptName }));
|
||||
}
|
||||
if (matches.empty())
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_NO_INSTANCES_MATCH);
|
||||
return false;
|
||||
}
|
||||
auto it = matches.rbegin();
|
||||
uint32 maxCount = it->first;
|
||||
mapid = it->second.first;
|
||||
if (++it != matches.rend() && 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.c_str());
|
||||
while (++it != matches.rend() && it->first == maxCount);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
handler->SendSysMessage(LANG_COMMAND_NO_INSTANCES_MATCH);
|
||||
return false;
|
||||
}
|
||||
auto it = matches.rbegin();
|
||||
uint32 maxCount = it->first;
|
||||
mapid = it->second.first;
|
||||
if (++it != matches.rend() && 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->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT(mapid);
|
||||
|
||||
@@ -35,6 +35,8 @@ EndScriptData */
|
||||
#include "RBAC.h"
|
||||
#include "WorldSession.h"
|
||||
|
||||
using namespace Trinity::ChatCommands;
|
||||
|
||||
class tele_commandscript : public CommandScript
|
||||
{
|
||||
public:
|
||||
@@ -57,17 +59,12 @@ public:
|
||||
return commandTable;
|
||||
}
|
||||
|
||||
static bool HandleTeleAddCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleTeleAddCommand(ChatHandler* handler, std::string const& name)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
if (!player)
|
||||
return false;
|
||||
|
||||
std::string name = args;
|
||||
|
||||
if (sObjectMgr->GetGameTeleExactName(name))
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_TP_ALREADYEXIST);
|
||||
@@ -97,13 +94,8 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleTeleDelCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleTeleDelCommand(ChatHandler* handler, GameTele const* tele)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
|
||||
GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
|
||||
if (!tele)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
|
||||
@@ -210,10 +202,14 @@ public:
|
||||
}
|
||||
|
||||
//Teleport group to given game_tele.entry
|
||||
static bool HandleTeleGroupCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleTeleGroupCommand(ChatHandler* handler, GameTele const* tele)
|
||||
{
|
||||
if (!*args)
|
||||
if (!tele)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* target = handler->getSelectedPlayer();
|
||||
if (!target)
|
||||
@@ -227,15 +223,6 @@ public:
|
||||
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
|
||||
return false;
|
||||
|
||||
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
|
||||
GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
|
||||
if (!tele)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
MapEntry const* map = sMapStore.LookupEntry(tele->mapId);
|
||||
if (!map || map->IsBattlegroundOrArena())
|
||||
{
|
||||
@@ -289,15 +276,8 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleTeleCommand(ChatHandler* handler, char const* args)
|
||||
static bool HandleTeleCommand(ChatHandler* handler, GameTele const* tele)
|
||||
{
|
||||
if (!*args)
|
||||
return false;
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
|
||||
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
|
||||
GameTele const* tele = handler->extractGameTeleFromLink((char*)args);
|
||||
if (!tele)
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
|
||||
@@ -305,6 +285,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = handler->GetSession()->GetPlayer();
|
||||
if (player->IsInCombat() && !handler->GetSession()->HasPermission(rbac::RBAC_PERM_COMMAND_TELE_NAME))
|
||||
{
|
||||
handler->SendSysMessage(LANG_YOU_IN_COMBAT);
|
||||
|
||||
Reference in New Issue
Block a user