diff --git a/playerbot/ChatHelper.cpp b/playerbot/ChatHelper.cpp index 57c1c031..fb528494 100644 --- a/playerbot/ChatHelper.cpp +++ b/playerbot/ChatHelper.cpp @@ -315,6 +315,22 @@ std::set ChatHelper::ExtractAllItemIds(const std::string& text) return ids; } +std::set ChatHelper::ExtractAllSkillIds(const std::string& text) +{ + std::set ids; + + std::regex rgx("Hskill:[0-9]+"); + auto begin = std::sregex_iterator(text.begin(), text.end(), rgx); + auto end = std::sregex_iterator(); + for (std::sregex_iterator i = begin; i != end; ++i) + { + std::smatch match = *i; + ids.insert(std::stoi(match.str().erase(0, 7))); + } + + return ids; +} + ItemIds ChatHelper::parseItems(const std::string& text, bool validate) { std::vector itemIDsUnordered = parseItemsUnordered(text, validate); diff --git a/playerbot/ChatHelper.h b/playerbot/ChatHelper.h index 0b2c27d7..dffa5b6a 100644 --- a/playerbot/ChatHelper.h +++ b/playerbot/ChatHelper.h @@ -26,6 +26,7 @@ namespace ai static std::set ExtractAllQuestIds(const std::string& text); static std::set ExtractAllItemIds(const std::string& text); + static std::set ExtractAllSkillIds(const std::string& text); static std::string formatQuest(Quest const* quest); diff --git a/playerbot/strategy/actions/ChatActionContext.h b/playerbot/strategy/actions/ChatActionContext.h index 13d8a5d9..383d9a06 100644 --- a/playerbot/strategy/actions/ChatActionContext.h +++ b/playerbot/strategy/actions/ChatActionContext.h @@ -75,6 +75,7 @@ #include "ValueActions.h" #include "QuestRewardActions.h" #include "ChooseTravelTargetAction.h" +#include "SkillAction.h" namespace ai { @@ -206,6 +207,7 @@ namespace ai creators["jump"] = &ChatActionContext::jump; creators["doquest"] = [](PlayerbotAI* ai) { return new FocusTravelTargetAction(ai); }; + creators["skill"] = [](PlayerbotAI* ai) { return new SkillAction(ai); }; } private: diff --git a/playerbot/strategy/actions/SkillAction.cpp b/playerbot/strategy/actions/SkillAction.cpp new file mode 100644 index 00000000..b0cb7453 --- /dev/null +++ b/playerbot/strategy/actions/SkillAction.cpp @@ -0,0 +1,136 @@ + +#include "playerbot/playerbot.h" +#include "SkillAction.h" +#include "Tools/Language.h" + +using namespace ai; + +bool SkillAction::Execute(Event& event) +{ + Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); + std::string cmd = event.getParam(); + + bool unlearn = (cmd.find("unlearn ") == 0); + + std::string skillName = cmd; + + if (unlearn) + skillName = skillName.substr(8); + + std::wstring wnamepart; + + if (!Utf8toWStr(skillName, wnamepart)) + return false; + + std::set skillIds = ChatHelper::ExtractAllSkillIds(skillName); + + // converting string that we try to find to lower case + wstrToLower(wnamepart); + + bool skillFound = false; + std::map args; + + for (uint32 id = 0; id < sSkillLineStore.GetNumRows(); ++id) + { + if (!bot->HasSkill(id)) + continue; + + SkillLineEntry const* skillInfo = sSkillLineStore.LookupEntry(id); + if (skillInfo) + { + int loc = requester->GetSession()->GetSessionDbcLocale(); + + std::string name = skillInfo->name[loc]; + + if (!skillName.empty() && skillIds.empty()) + { + if (name.empty()) + continue; + + if (!Utf8FitTo(name, wnamepart)) + { + loc = 0; + for (; loc < MAX_LOCALE; ++loc) + { + if (loc == requester->GetSession()->GetSessionDbcLocale()) + continue; + + name = skillInfo->name[loc]; + if (name.empty()) + continue; + + if (Utf8FitTo(name, wnamepart)) + break; + } + } + } + + if (skillName.empty() || skillIds.find(id) != skillIds.end() || (loc < MAX_LOCALE && skillIds.empty())) + { + if (unlearn) + { + args["%skillname"] = name; + + if (!bot->GetSkillInfo(uint16(id), ([](SkillRaceClassInfoEntry const& entry) { return (entry.flags & SKILL_FLAG_CAN_UNLEARN); }))) + { + ai->TellPlayerNoFacing(requester, BOT_TEXT2("Unable to unlearn %skillname", args)); + return false; + } + + bot->SetSkillStep(uint16(id), 0); + + ai->TellPlayerNoFacing(requester, BOT_TEXT2("Unlearned %skillname", args)); + + return true; + } + + char valStr[50] = ""; + char const* knownStr = ""; + knownStr = requester->GetSession()->GetMangosString(LANG_KNOWN); + uint32 curValue = bot->GetSkillValuePure(id); + uint32 maxValue = bot->GetSkillMaxPure(id); + uint32 permValue = bot->GetSkillBonusPermanent(id); + uint32 tempValue = bot->GetSkillBonusTemporary(id); + + char const* valFormat = requester->GetSession()->GetMangosString(LANG_SKILL_VALUES); + snprintf(valStr, 50, valFormat, curValue, maxValue, permValue, tempValue); + std::ostringstream out; + out << "|cffffffff|Hskill:"; + out << id; + out << "|h["; + out << name; + out << "]|h|r ("; + + out << curValue << "/" << maxValue; + + if (permValue) + out << " +perm " << permValue; + + if (tempValue) + out << " +temp " << permValue; + + out << ")"; + + ai->TellPlayerNoFacing(requester, out); + + skillFound = true; + } + } + } + + if (!skillFound) + { + if (skillName.empty()) + { + ai->TellPlayerNoFacing(requester, BOT_TEXT2("No skills found.", args)); + } + else + { + args["%skillname"] = skillName; + ai->TellPlayerNoFacing(requester, BOT_TEXT2("Skill %skillname not found.", args)); + } + return false; + } + + return true; +} diff --git a/playerbot/strategy/actions/SkillAction.h b/playerbot/strategy/actions/SkillAction.h new file mode 100644 index 00000000..7ac6429e --- /dev/null +++ b/playerbot/strategy/actions/SkillAction.h @@ -0,0 +1,27 @@ +#pragma once +#include "playerbot/LootObjectStack.h" +#include "GenericActions.h" + +namespace ai +{ + class SkillAction : public ChatCommandAction + { + public: + SkillAction(PlayerbotAI* ai) : ChatCommandAction(ai, "skill") {} + virtual bool Execute(Event& event) override; + +#ifdef GenerateBotHelp + virtual std::string GetHelpName() { return "skill"; } //Must equal iternal name + virtual std::string GetHelpDescription() + { + return "This chat command gives information about a bot\'s skills.\n" + "Examples:\n" + "skill : List all skills and their current level.\n" + "skill [name] : List the skill level of a current skill.\n" + "ss unlearn [name] : Unlearns a primary profession.\n"; + } + virtual std::vector GetUsedActions() { return {}; } + virtual std::vector GetUsedValues() { return {}; } +#endif + }; +} diff --git a/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp b/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp index 398cb6e9..3e42e4f9 100644 --- a/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/playerbot/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -106,6 +106,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* ai) : PassTr supported.push_back("move style"); supported.push_back("jump"); supported.push_back("doquest"); + supported.push_back("skill"); } void ChatCommandHandlerStrategy::InitReactionTriggers(std::list &triggers) diff --git a/playerbot/strategy/triggers/ChatTriggerContext.h b/playerbot/strategy/triggers/ChatTriggerContext.h index 119fd9f1..ba0f5a44 100644 --- a/playerbot/strategy/triggers/ChatTriggerContext.h +++ b/playerbot/strategy/triggers/ChatTriggerContext.h @@ -138,6 +138,7 @@ namespace ai creators["move style"] = &ChatTriggerContext::move_style; creators["jump"] = &ChatTriggerContext::jump; creators["doquest"] = [](PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "doquest"); }; + creators["skill"] = [](PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "skill"); }; } private: