#include "ScriptPCH.h" #include "DatabaseEnv.h" #include "Util.h" #include "World.h" #include "ObjectMgr.h" #include #include #include #include constexpr bool LoadNonFreeSpells = true; namespace LevelUpActions { const std::unordered_map> trainers = { {CLASS_WARRIOR, {26332, 26332}}, {CLASS_PALADIN, {20406, 20406}}, {CLASS_DRUID, {26324, 12042}}, {CLASS_MAGE, {26326, 28956}}, {CLASS_WARLOCK, {23534, 16266}}, {CLASS_PRIEST, {16660, 17510}}, {CLASS_HUNTER, {17505, 16673}}, {CLASS_ROGUE, {26329, 26329}}, {CLASS_SHAMAN, {17520, 17520}}, {CLASS_DEATH_KNIGHT, {31084, 31084}} }; enum class ActionType : uint16 { LearnSpell = 1, LearnAllSpells, ApplyAura, ModifyMoney, AddItem }; class Action { public: virtual void Process(Player*) = 0; virtual ~Action() {}; }; namespace detail { template class Action_Impl : public Action { public: Action_Impl(std::vector&& params) : params(params) {} void Process(Player* player) { static_cast(this)->Process(player); } protected: std::vector params; }; #define DEFINE_TAG(x) \ struct x {} x##_; DEFINE_TAG(float_tag); DEFINE_TAG(int32_tag); DEFINE_TAG(uint32_tag); DEFINE_TAG(vector_uint32_tag); #define EXTRACT_F(x) void ExtractOne(std::vector& v, Tokenizer& tok, std::size_t i, x) EXTRACT_F(float_tag) { v.push_back(boost::any{ std::stof(tok[i]) }); } EXTRACT_F(int32_tag) { v.push_back(boost::any{ std::stoi(tok[i]) }); } EXTRACT_F(uint32_tag) { auto res = std::stoi(tok[i]); if (res < 0) res = 0; v.push_back(boost::any{ static_cast(res) }); } EXTRACT_F(vector_uint32_tag) { std::vector vec; for (uint32 k = i; k < tok.size(); ++k, ++i) { vec.push_back(std::stoi(tok[k])); } v.push_back(boost::any{ vec }); } template void Extract(std::vector& v, Tokenizer& tok, std::size_t i, Type type, Types... types) { ExtractOne(v, tok, i++, type); Extract(v, tok, i, types...); } template void Extract(std::vector& v, Tokenizer& tok, std::size_t i) {} template std::vector ExtractTypes(std::string params, std::size_t i, Types... types) { Tokenizer tokenizer{ params, ' ' }; std::vector ret; if(tokenizer.size()) Extract(ret, tokenizer, 0, types...); return ret; } } #define CLASS_IMPL(x) class x : public detail::Action_Impl CLASS_IMPL(LearnSpellAction) { public: using Action_Impl::Action_Impl; void Process(Player* player) { uint32 spellId = boost::any_cast(params[0]); if (!player->HasSpell(spellId)) player->LearnSpell(spellId, false); } }; CLASS_IMPL(LearnAllSpellsAction) { public: using Action_Impl::Action_Impl; void Process(Player* player) { auto itr = trainers.find(player->getClass()); if(itr != trainers.end()) { for (const auto& trainerId : itr->second) { auto spells = sObjectMgr->GetNpcTrainerSpells(trainerId); if (spells) { for (const auto& spell : spells->spellList) { const auto& sp = spell.second; if (sp.MoneyCost && !LoadNonFreeSpells) continue; if (!sp.ReqSkillLine && !sp.ReqSkillRank && sp.ReqLevel <= player->getLevel() && !player->HasSpell(sp.SpellID)) player->LearnSpell(sp.SpellID, false); } } } } } }; CLASS_IMPL(ApplyAuraAction) { public: using Action_Impl::Action_Impl; void Process(Player* player) { uint32 spellId = boost::any_cast(params[0]); player->AddAura(spellId, player); } }; CLASS_IMPL(ModifyMoneyAction) { public: using Action_Impl::Action_Impl; void Process(Player* player) { int32 money = boost::any_cast(params[0]); player->ModifyMoney(money); } }; CLASS_IMPL(AddItemAction) { public: using Action_Impl::Action_Impl; void Process(Player* player) { uint32 itemId = boost::any_cast(params[0]); uint32 count = boost::any_cast(params[1]); player->AddItem(itemId, count); } }; const std::unordered_map(std::string params)>> typeFactory = { { ActionType::LearnSpell, [] (std::string params) { auto vec = detail::ExtractTypes(params, 0, detail::uint32_tag_); return std::unique_ptr(new LearnSpellAction(std::move(vec))); } }, { ActionType::LearnAllSpells, [] (std::string params) { auto vec = detail::ExtractTypes(params, 0, detail::vector_uint32_tag_); return std::unique_ptr(new LearnAllSpellsAction(std::move(vec))); } }, { ActionType::ApplyAura, [] (std::string params) { auto vec = detail::ExtractTypes(params, 0, detail::uint32_tag_); return std::unique_ptr(new ApplyAuraAction(std::move(vec))); } }, { ActionType::ModifyMoney, [] (std::string params) { auto vec = detail::ExtractTypes(params, 0, detail::int32_tag_); return std::unique_ptr(new ModifyMoneyAction(std::move(vec))); } }, { ActionType::AddItem, [] (std::string params) { auto vec = detail::ExtractTypes(params, 0, detail::uint32_tag_, detail::uint32_tag_); return std::unique_ptr(new AddItemAction(std::move(vec))); } } }; std::unordered_multimap> actionHolder; class OnLevelupActions : public PlayerScript { public: OnLevelupActions() : PlayerScript("OnLevelupSpells") {} void OnLevelChanged(Player* player, uint8 oldLevel) override { int32 newLevel = player->getLevel(); auto levelPairItr = actionHolder.equal_range(newLevel); auto autoPairItr = actionHolder.equal_range(-1); if (levelPairItr.first != levelPairItr.second) { while (levelPairItr.first != levelPairItr.second) { levelPairItr.first->second->Process(player); ++levelPairItr.first; } } if (autoPairItr.first != autoPairItr.second) { while (autoPairItr.first != autoPairItr.second) { autoPairItr.first->second->Process(player); ++autoPairItr.first; } } } }; class LoadActions : public WorldScript { public: LoadActions() : WorldScript("LoadActionsLevelup") {} void OnStartup() override { const auto actionResult = WorldDatabase.Query("SELECT `Level`, `Action`, `Params` FROM `levelup_actions`"); if (!actionResult) return; do { Field* fields = actionResult->Fetch(); int32 level = fields[0].GetInt32(); uint32 action = fields[1].GetUInt32(); std::string params = fields[2].GetString(); auto itr = typeFactory.find(static_cast(action)); if (itr != typeFactory.end()) actionHolder.insert({ level, itr->second(params) }); } while (actionResult->NextRow()); } }; } void AddSC_LevelUpActions() { new LevelUpActions::OnLevelupActions; new LevelUpActions::LoadActions; }