282 lines
9.3 KiB
C++
282 lines
9.3 KiB
C++
#include <iostream>
|
|
#include <string>
|
|
#include <set>
|
|
#include <numeric>
|
|
|
|
#include "mhache_factory.h"
|
|
#include "mhache_skill.h"
|
|
#include "mhache_color.h"
|
|
|
|
MHacheFactory&
|
|
MHacheFactory::singleton()
|
|
{
|
|
static MHacheFactory factory;
|
|
return factory;
|
|
}
|
|
|
|
bool
|
|
MHacheFactory::parse_root_table(const std::filesystem::path& tomlfile, toml::table& root)
|
|
{
|
|
auto result = toml::parse_file(tomlfile.u8string());
|
|
|
|
if (!result) {
|
|
std::cerr << "Parsing of TOML file << " << tomlfile << " failed." << std::endl;
|
|
std::cerr << "-> " << result.error().description() << std::endl;
|
|
return false;
|
|
}
|
|
|
|
root = std::move(result.table());
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MHacheFactory::load_skills(const std::filesystem::path& tomlfile)
|
|
{
|
|
toml::table skills;
|
|
{
|
|
toml::table root;
|
|
if (!parse_root_table(tomlfile, root) || root.empty())
|
|
return false;
|
|
|
|
if (!deserialize_table_node<toml::table>(root, "skills", skills))
|
|
return false;
|
|
}
|
|
|
|
skills.for_each([this](const toml::key& skill_name, const toml::table& skill)
|
|
{
|
|
const std::string name = std::string(skill_name.str());
|
|
if (name.empty()) return false;
|
|
|
|
MHacheColor* color = nullptr;
|
|
{
|
|
std::string skill_color;
|
|
if (!deserialize_table_node(skill, "color", skill_color))
|
|
return false;
|
|
|
|
color = MHacheColor::get_color(skill_color);
|
|
} if (!color) return false;
|
|
|
|
std::string description;
|
|
if (!deserialize_table_node(skill, "description", description) || description.empty())
|
|
return false;
|
|
|
|
std::vector<std::string> levels;
|
|
if (!deserialize_table_node(skill, "levels", levels) || levels.empty())
|
|
return false;
|
|
|
|
_skills.emplace(name, description, color, std::move(levels));
|
|
|
|
return true;
|
|
});
|
|
|
|
return !(_skills.empty());
|
|
}
|
|
|
|
bool
|
|
MHacheFactory::load_armors(const std::filesystem::path& tomlfile)
|
|
{
|
|
toml::table sets;
|
|
{
|
|
toml::table root;
|
|
if (!parse_root_table(tomlfile, root) || root.empty())
|
|
return false;
|
|
|
|
if (!deserialize_table_node(root, "sets", sets))
|
|
return false;
|
|
}
|
|
|
|
sets.for_each([this](const toml::key& set_name, const toml::table& set)
|
|
{
|
|
const std::string name = std::string(set_name.str());
|
|
if (name.empty()) return false;
|
|
|
|
unsigned char rarity;
|
|
if (!deserialize_table_node(set, "rarity", rarity))
|
|
return false;
|
|
|
|
unsigned short defense;
|
|
if (!deserialize_table_node(set, "defense", defense))
|
|
return false;
|
|
|
|
MHacheArmor::ElementalResistances resistances;
|
|
if (!deserialize_table_node(set, "resistances", resistances))
|
|
return false;
|
|
|
|
// Prepare the armor
|
|
MHacheArmor armor(name, rarity, defense, std::move(resistances));
|
|
|
|
// Attach its parts
|
|
for (size_t label = 0; label < MHacheArmor::Part::Label::count; ++label)
|
|
{
|
|
toml::table part;
|
|
if (!deserialize_table_node(set, MHacheArmor::Part::label_names[label], part))
|
|
continue;
|
|
|
|
std::string part_name;
|
|
if (!deserialize_table_node(part, "name", part_name))
|
|
continue;
|
|
|
|
std::vector<unsigned char> part_slots;
|
|
if (!deserialize_table_node(part, "slots", part_slots))
|
|
continue;
|
|
|
|
toml::table part_skills;
|
|
if (!deserialize_table_node(part, "skills", part_skills))
|
|
continue;
|
|
|
|
armor[label]
|
|
.is_called(part_name)
|
|
.define_jewel_slots(std::move(part_slots));
|
|
|
|
part_skills.for_each([this, &armor, label](const toml::key& skill_name, toml::value<int64_t> skill_level)
|
|
{
|
|
const auto it_skill = _skills.find(skill_name.str());
|
|
|
|
if ((_skills.end() == it_skill) || 0 >= skill_level)
|
|
return false;
|
|
|
|
armor[label].add_skill(&(*it_skill), static_cast<unsigned char>(skill_level.get()));
|
|
return true;
|
|
});
|
|
|
|
if (armor[label].skills().empty())
|
|
{
|
|
armor[label].detach();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (armor.attached_parts().size() > 0)
|
|
_armors.insert(std::move(armor));
|
|
|
|
return true;
|
|
});
|
|
|
|
return !_armors.empty();
|
|
}
|
|
|
|
void
|
|
MHacheFactory::find_builds(const MappedSkillLevels& skills_levels) const
|
|
{
|
|
std::array<
|
|
std::set<const MHacheArmor::Part*, PtrLessComparator<MHacheArmor::Part>>,
|
|
MHacheArmor::Part::Label::count
|
|
> armors_parts;
|
|
|
|
for (const auto& skill_level: skills_levels)
|
|
{
|
|
const MHacheSkill* p_skill = skill_level.first;
|
|
if (!p_skill) continue;
|
|
|
|
const std::string_view skill_name = p_skill->name();
|
|
|
|
for (const MHacheArmor& armor: _armors) {
|
|
const auto matching_parts = armor.search_for_skill(skill_name);
|
|
for (const size_t idx: matching_parts)
|
|
armors_parts[idx].insert(&armor[idx]);
|
|
}
|
|
}
|
|
|
|
if (armors_parts.empty())
|
|
return;
|
|
|
|
MappedSkillLevels build;
|
|
std::vector<std::array<const MHacheArmor::Part*, MHacheArmor::Part::count>> results;
|
|
std::array<const MHacheArmor::Part*, MHacheArmor::Part::count> result;
|
|
|
|
auto accumulate_skills = [](const MappedSkillLevels& AdditionalSkills, MappedSkillLevels& OutputSkills)
|
|
{
|
|
for (const auto& s: AdditionalSkills)
|
|
OutputSkills[s.first] += s.second;
|
|
};
|
|
|
|
auto is_matching_build = [](const MappedSkillLevels& Skills, const MappedSkillLevels& ExpectedSkills)
|
|
{
|
|
size_t cpt = 0;
|
|
|
|
for (const auto& s: ExpectedSkills) {
|
|
auto it = Skills.find(s.first);
|
|
if (Skills.end() == it) break;
|
|
if (it->second >= s.second) ++cpt;
|
|
}
|
|
|
|
return (cpt == ExpectedSkills.size());
|
|
};
|
|
|
|
std::array<size_t, MHacheArmor::Part::count> parts_modulo;
|
|
parts_modulo[MHacheArmor::Part::LEGS] = 1;
|
|
|
|
for (size_t i = (MHacheArmor::Part::count - 1); i-- > 0;)
|
|
{
|
|
parts_modulo[i] = (armors_parts[i + 1].empty() ? 1 : armors_parts[i + 1].size()) * parts_modulo[i + 1];
|
|
}
|
|
|
|
/*
|
|
const size_t modulo_legs = 1;
|
|
const size_t modulo_waist = (armors_parts[MHacheArmor::Part::LEGS ].empty() ? 1 : armors_parts[MHacheArmor::Part::LEGS ].size()) * modulo_legs;
|
|
const size_t modulo_arms = (armors_parts[MHacheArmor::Part::WAIST].empty() ? 1 : armors_parts[MHacheArmor::Part::WAIST].size()) * modulo_waist;
|
|
const size_t modulo_chest = (armors_parts[MHacheArmor::Part::ARMS ].empty() ? 1 : armors_parts[MHacheArmor::Part::ARMS ].size()) * modulo_arms;
|
|
const size_t modulo_head = (armors_parts[MHacheArmor::Part::CHEST].empty() ? 1 : armors_parts[MHacheArmor::Part::CHEST].size()) * modulo_chest;
|
|
*/
|
|
|
|
const size_t combinations =
|
|
(armors_parts[MHacheArmor::Part::HEAD ].empty() ? 1 : armors_parts[MHacheArmor::Part::HEAD ].size()) * parts_modulo[MHacheArmor::Part::HEAD];
|
|
|
|
std::array<
|
|
std::set<const MHacheArmor::Part*, PtrLessComparator<MHacheArmor::Part>>::iterator,
|
|
MHacheArmor::Part::count
|
|
> parts_iterator;
|
|
|
|
for (unsigned char label = 0; label < MHacheArmor::Part::count; ++label)
|
|
parts_iterator[label] = armors_parts[label].begin();
|
|
|
|
/*
|
|
std::set<const MHacheArmor::Part*, PtrLessComparator<MHacheArmor::Part>>::iterator it_head = armors_parts[MHacheArmor::Part::HEAD].begin();
|
|
std::set<const MHacheArmor::Part*, PtrLessComparator<MHacheArmor::Part>>::iterator it_chest = armors_parts[MHacheArmor::Part::CHEST].begin();
|
|
std::set<const MHacheArmor::Part*, PtrLessComparator<MHacheArmor::Part>>::iterator it_arms = armors_parts[MHacheArmor::Part::ARMS].begin();
|
|
std::set<const MHacheArmor::Part*, PtrLessComparator<MHacheArmor::Part>>::iterator it_waist = armors_parts[MHacheArmor::Part::WAIST].begin();
|
|
std::set<const MHacheArmor::Part*, PtrLessComparator<MHacheArmor::Part>>::iterator it_legs = armors_parts[MHacheArmor::Part::LEGS].begin();
|
|
*/
|
|
|
|
for (size_t i = 0; i < combinations; ++i)
|
|
{
|
|
build = {};
|
|
result = {};
|
|
|
|
for (unsigned char label = 0; label < MHacheArmor::Part::count; ++label)
|
|
{
|
|
if (!armors_parts[label].empty() && (0 == (i % parts_modulo[label])))
|
|
{
|
|
if (std::next(parts_iterator[label]) == armors_parts[label].end()) {
|
|
parts_iterator[label] = armors_parts[label].begin();
|
|
}
|
|
else
|
|
parts_iterator[label] = std::next(parts_iterator[label]);
|
|
}
|
|
|
|
if (parts_iterator[label] != armors_parts[label].end()) {
|
|
accumulate_skills((*parts_iterator[label])->skills(), build);
|
|
result[label] = (*parts_iterator[label]);
|
|
|
|
if (is_matching_build(build, skills_levels)) {
|
|
results.push_back(std::move(result));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const auto& r: results) {
|
|
MappedSkillLevels rlevels;
|
|
|
|
for (unsigned char i = 0; i < MHacheArmor::Part::count; ++i)
|
|
{
|
|
accumulate_skills(r[i]->skills(), rlevels);
|
|
}
|
|
|
|
for (const auto& s: rlevels) {
|
|
std::cerr << s.first->name() << " -> " << s.second << std::endl;
|
|
}
|
|
}
|
|
}
|