#include #include #include #include #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(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 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 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 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(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>, 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> results; std::array 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 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>::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>::iterator it_head = armors_parts[MHacheArmor::Part::HEAD].begin(); std::set>::iterator it_chest = armors_parts[MHacheArmor::Part::CHEST].begin(); std::set>::iterator it_arms = armors_parts[MHacheArmor::Part::ARMS].begin(); std::set>::iterator it_waist = armors_parts[MHacheArmor::Part::WAIST].begin(); std::set>::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; } } }