#include "ScriptPCH.h"
#include "DatabaseEnv.h"
#include "Util.h"
#include "World.h"
#include "ObjectMgr.h"
#include <unordered_map>
#include <memory>
#include <boost/any.hpp>
#include <functional>
constexpr bool LoadNonFreeSpells = true;
namespace LevelUpActions
{
const std::unordered_map<uint32, std::array<uint32, 2>> 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 <typename D>
class Action_Impl : public Action
{
public:
Action_Impl(std::vector<boost::any>&& params) : params(params) {}
void Process(Player* player)
{
static_cast<D*>(this)->Process(player);
}
protected:
std::vector<boost::any> 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<boost::any>& 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<uint32>(res) });
}
EXTRACT_F(vector_uint32_tag)
{
std::vector<uint32> vec;
for (uint32 k = i; k < tok.size(); ++k, ++i)
{
vec.push_back(std::stoi(tok[k]));
}
v.push_back(boost::any{ vec });
}
template <typename Type, typename... Types>
void Extract(std::vector<boost::any>& v, Tokenizer& tok, std::size_t i, Type type, Types... types)
{
ExtractOne(v, tok, i++, type);
Extract(v, tok, i, types...);
}
template <typename... Types>
void Extract(std::vector<boost::any>& v, Tokenizer& tok, std::size_t i) {}
template <typename... Types>
std::vector<boost::any> ExtractTypes(std::string params, std::size_t i, Types... types)
{
Tokenizer tokenizer{ params, ' ' };
std::vector<boost::any> ret;
if(tokenizer.size())
Extract<Types...>(ret, tokenizer, 0, types...);
return ret;
}
}
#define CLASS_IMPL(x) class x : public detail::Action_Impl<x>
CLASS_IMPL(LearnSpellAction)
{
public:
using Action_Impl::Action_Impl;
void Process(Player* player)
{
uint32 spellId = boost::any_cast<uint32>(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<uint32>(params[0]);
player->AddAura(spellId, player);
}
};
CLASS_IMPL(ModifyMoneyAction)
{
public:
using Action_Impl::Action_Impl;
void Process(Player* player)
{
int32 money = boost::any_cast<int32>(params[0]);
player->ModifyMoney(money);
}
};
CLASS_IMPL(AddItemAction)
{
public:
using Action_Impl::Action_Impl;
void Process(Player* player)
{
uint32 itemId = boost::any_cast<uint32>(params[0]);
uint32 count = boost::any_cast<uint32>(params[1]);
player->AddItem(itemId, count);
}
};
const std::unordered_map<ActionType, std::function<std::unique_ptr<Action>(std::string params)>> typeFactory =
{
{
ActionType::LearnSpell, [] (std::string params) {
auto vec = detail::ExtractTypes(params, 0, detail::uint32_tag_);
return std::unique_ptr<Action>(new LearnSpellAction(std::move(vec))); }
},
{
ActionType::LearnAllSpells, [] (std::string params) {
auto vec = detail::ExtractTypes(params, 0, detail::vector_uint32_tag_);
return std::unique_ptr<Action>(new LearnAllSpellsAction(std::move(vec))); }
},
{
ActionType::ApplyAura, [] (std::string params) {
auto vec = detail::ExtractTypes(params, 0, detail::uint32_tag_);
return std::unique_ptr<Action>(new ApplyAuraAction(std::move(vec))); }
},
{
ActionType::ModifyMoney, [] (std::string params) {
auto vec = detail::ExtractTypes(params, 0, detail::int32_tag_);
return std::unique_ptr<Action>(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<Action>(new AddItemAction(std::move(vec))); }
}
};
std::unordered_multimap<int32, std::unique_ptr<Action>> 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<ActionType>(action));
if (itr != typeFactory.end())
actionHolder.insert({ level, itr->second(params) });
} while (actionResult->NextRow());
}
};
}
void AddSC_LevelUpActions()
{
new LevelUpActions::OnLevelupActions;
new LevelUpActions::LoadActions;
}
Comments