diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp index 16a750e1314f5..b00b48fe0a093 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp +++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp @@ -18,10 +18,25 @@ #include "ChatCommandArgs.h" #include "ChatCommand.h" #include "ChatCommandHyperlinks.h" +#include "DB2Stores.h" #include "ObjectMgr.h" using namespace Trinity::ChatCommands; +struct AchievementVisitor +{ + using value_type = AchievementEntry const*; + value_type operator()(Hyperlink achData) const { return achData->achievement; } + value_type operator()(uint32 achId) const { return sAchievementStore.LookupEntry(achId); } +}; +char const* Trinity::ChatCommands::ArgInfo::TryConsume(AchievementEntry const*& data, char const* args) +{ + Variant , uint32> val; + if ((args = CommandArgsConsumerSingle::TryConsumeTo(val, args))) + data = boost::apply_visitor(AchievementVisitor(), val); + return args; +} + struct GameTeleVisitor { using value_type = GameTele const*; diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h index 811981e124411..bbdb2565f25af 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h +++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h @@ -108,6 +108,13 @@ struct ArgInfo>> } }; +// AchievementEntry* from numeric id or link +template <> +struct TC_GAME_API ArgInfo +{ + static char const* TryConsume(AchievementEntry const*&, char const*); +}; + // GameTele* from string name or link template <> struct TC_GAME_API ArgInfo diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h index f099d7eac5734..42c0a195041d3 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h +++ b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h @@ -24,7 +24,7 @@ namespace Trinity { namespace ChatCommands { -static const char COMMAND_DELIMITER = ' '; +static constexpr char COMMAND_DELIMITER = ' '; /***************** HELPERS *************************\ |* These really aren't for outside use... *| diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.cpp b/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.cpp new file mode 100644 index 0000000000000..53035813769fb --- /dev/null +++ b/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * + * 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 . + */ + +#include "ChatCommandHyperlinks.h" +#include "DB2Stores.h" + +static constexpr char HYPERLINK_DELIMITER = ':'; + +class AchievementLinkTokenizer +{ + public: + AchievementLinkTokenizer(char const* pos, size_t len) : _pos(pos), _len(len), _empty(false) {} + + template + bool TryConsumeTo(T& val) + { + if (_empty) + return false; + + char const* firstPos = _pos; + size_t thisLen = 0; + // find next delimiter + for (; _len && *_pos != HYPERLINK_DELIMITER; --_len, ++_pos, ++thisLen); + if (_len) + --_len, ++_pos; // skip the delimiter + else + _empty = true; + + return Trinity::ChatCommands::base_tag::StoreTo(val, firstPos, thisLen); + } + + bool IsEmpty() { return _empty; } + + private: + char const* _pos; + size_t _len; + bool _empty; +}; + +bool Trinity::ChatCommands::achievement::StoreTo(AchievementLinkData& val, char const* pos, size_t len) +{ + AchievementLinkTokenizer t(pos, len); + uint32 achievementId; + if (!t.TryConsumeTo(achievementId)) + return false; + val.achievement = sAchievementStore.LookupEntry(achievementId); + return val.achievement && t.TryConsumeTo(val.characterId) && t.TryConsumeTo(val.isFinished) && + t.TryConsumeTo(val.month) && t.TryConsumeTo(val.day) && t.TryConsumeTo(val.year) && t.TryConsumeTo(val.criteria[0]) && + t.TryConsumeTo(val.criteria[1]) && t.TryConsumeTo(val.criteria[2]) && t.TryConsumeTo(val.criteria[3]) && t.IsEmpty(); +} diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h b/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h index f3fcf74ecf415..bb4d8727f1677 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h +++ b/src/server/game/Chat/ChatCommands/ChatCommandHyperlinks.h @@ -22,6 +22,8 @@ #include "ChatCommandTags.h" #include "ObjectGuid.h" +struct AchievementEntry; + namespace Trinity { namespace ChatCommands { @@ -34,6 +36,8 @@ struct Hyperlink : public ContainerTag public: operator value_type() const { return val; } + value_type const& operator*() const { return val; } + value_type const* operator->() const { return &val; } char const* TryConsume(char const* pos) { @@ -117,7 +121,7 @@ struct base_tag } }; -#define make_base_tag(ltag, type) struct ltag : public base_tag { typedef type value_type; static constexpr char const* tag() { return #ltag; } } +#define make_base_tag(ltag, type) struct ltag : public base_tag { using value_type = 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); @@ -126,6 +130,24 @@ make_base_tag(taxinode, uint32); make_base_tag(tele, uint32); #undef make_base_tag +struct AchievementLinkData +{ + AchievementEntry const* achievement; + std::string characterId; // TODO: full ObjectGuid (implement parsing guid strings) + bool isFinished; + uint16 year; + uint8 month; + uint8 day; + uint32 criteria[4]; +}; + +struct TC_GAME_API achievement +{ + using value_type = AchievementLinkData; + static constexpr char const* tag() { return "achievement"; } + static bool StoreTo(AchievementLinkData& val, char const* pos, size_t len); +}; + }} #endif diff --git a/src/server/scripts/Commands/cs_achievement.cpp b/src/server/scripts/Commands/cs_achievement.cpp index 7d019f5258b1d..ba2c764e4eeca 100644 --- a/src/server/scripts/Commands/cs_achievement.cpp +++ b/src/server/scripts/Commands/cs_achievement.cpp @@ -48,20 +48,8 @@ class achievement_commandscript : public CommandScript return commandTable; } - static bool HandleAchievementAddCommand(ChatHandler* handler, char const* args) + static bool HandleAchievementAddCommand(ChatHandler* handler, AchievementEntry const* achievementEntry) { - if (!*args) - return false; - - uint32 achievementId = atoi((char*)args); - if (!achievementId) - { - if (char* id = handler->extractKeyFromLink((char*)args, "Hachievement")) - achievementId = atoul(id); - if (!achievementId) - return false; - } - Player* target = handler->getSelectedPlayer(); if (!target) { @@ -69,9 +57,7 @@ class achievement_commandscript : public CommandScript handler->SetSentErrorMessage(true); return false; } - - if (AchievementEntry const* achievementEntry = sAchievementStore.LookupEntry(achievementId)) - target->CompletedAchievement(achievementEntry); + target->CompletedAchievement(achievementEntry); return true; }