#include "BuildSpecies.hpp" #include #include #define GET(o, objectKey, key) \ auto& _##objectKey = o.at(#objectKey); \ if (_##objectKey.is_null()) { \ auto errorKeyFunc = [=]() { return key; }; \ std::cout << "Failed to retrieve key '" << #objectKey << "' for object with key '" << errorKeyFunc() \ << "' in file '" << path << "'\n"; \ return nullptr; \ } PkmnLib::Library::SpeciesLibrary* BuildSpecies::BuildLibrary(const std::string& path, const CreatureLib::Library::TypeLibrary* types, const CreatureLib::Library::TalentLibrary* talentLibrary, const PkmnLib::Library::MoveLibrary* moveLibrary) { std::ifstream fileStream(path.c_str()); if (fileStream.fail()) { std::cout << "Failed to load Pokemon data file at '" << path << "'\n"; return nullptr; } auto lib = new PkmnLib::Library::SpeciesLibrary(); json j; fileStream >> j; for (const auto& it : j.items()) { if (it.key().starts_with("$")) { continue; } auto& val = it.value(); GET(val, id, it.key()); GET(val, species, it.key()); GET(val, genderRatio, it.key()); GET(val, growthRate, it.key()); GET(val, baseHappiness, it.key()); GET(val, catchRate, it.key()); GET(val, color, it.key()); GET(val, genderDifference, it.key()); GET(val, eggGroups, it.key()); GET(val, eggCycles, it.key()); GET(val, tags, it.key()); GET(val, formes, it.key()); PkmnLib::Library::PokemonSpecies* species = nullptr; ArbUt::List eggGroups; for (const auto& eg : _eggGroups) { eggGroups.Append(eg.get().c_str()); } auto& defaultForme = _formes["default"]; if (!defaultForme.is_null()) { auto forme = BuildForme("default", defaultForme, it.key(), path, types, talentLibrary, moveLibrary); species = new PkmnLib::Library::PokemonSpecies( _id.get(), ArbUt::StringView(_species.get().c_str()), forme, _genderRatio.get() / static_cast(100), ArbUt::StringView(_growthRate.get().c_str()), _catchRate.get(), _baseHappiness.get(), eggGroups); } for (const auto& formeIt : _formes.items()) { if (formeIt.key() == "default") { continue; } auto forme = BuildForme(formeIt.key(), formeIt.value(), it.key(), path, types, talentLibrary, moveLibrary); if (forme == nullptr) return nullptr; if (species == nullptr) { species = new PkmnLib::Library::PokemonSpecies( _id.get(), ArbUt::StringView(_species.get().c_str()), forme, static_cast(_genderRatio.get()) / static_cast(100), ArbUt::StringView(_growthRate.get().c_str()), _catchRate.get(), _baseHappiness.get(), eggGroups); } else { if (species->HasForme(ArbUt::StringView(formeIt.key().c_str()))) { std::cout << "Species '" << it.key() << "' has duplicate forme '" << formeIt.key() << "'. Skipping.\n"; delete forme; continue; } species->SetVariant(ArbUt::StringView(formeIt.key().c_str()), forme); } } if (species == nullptr) { std::cout << "Pokemon with key '" << it.key() << "' does not have any formes.\n"; return nullptr; } lib->Insert(ArbUt::StringView(it.key().c_str()), species); } return lib; } static CreatureLib::Library::StatisticSet ParseStatistics(const json& json) { return {json["hp"].get(), json["attack"].get(), json["defense"].get(), json["specialAttack"].get(), json["specialDefense"].get(), json["speed"].get()}; } inline void BuildLevelMove(const json& levelMoveObj, const PkmnLib::Library::MoveLibrary* moveLibrary, PkmnLib::Library::LearnableMoves* moves) { auto levelMoveName = levelMoveObj.at("name").get(); auto level = levelMoveObj.at("level").get(); auto move = moveLibrary->Get(ArbUt::StringView(levelMoveName.c_str(), levelMoveName.size())); moves->AddLevelAttack(level, move.ForceAs()); } const PkmnLib::Library::PokemonForme* BuildSpecies::BuildForme(const std::string& name, const json& forme, const std::string& baseKeyName, const std::string& path, const CreatureLib::Library::TypeLibrary* typeLibrary, const CreatureLib::Library::TalentLibrary* talentLibrary, const PkmnLib::Library::MoveLibrary* moveLibrary) { GET(forme, abilities, baseKeyName + " -> " + name); GET(forme, hiddenAbilities, baseKeyName + " -> " + name); GET(forme, baseStats, baseKeyName + " -> " + name); GET(forme, evReward, baseKeyName + " -> " + name); GET(forme, types, baseKeyName + " -> " + name); GET(forme, height, baseKeyName + " -> " + name); GET(forme, weight, baseKeyName + " -> " + name); GET(forme, baseExp, baseKeyName + " -> " + name); GET(forme, moves, baseKeyName + " -> " + name); auto types = ArbUt::List(_types.size()); for (const auto& t : _types.items()) { auto s = t.value().get(); types.Append(typeLibrary->GetTypeId(ArbUt::StringView(s.c_str(), s.length()))); } auto stats = ParseStatistics(_baseStats); auto abilities = ArbUt::List>(_abilities.size()); for (const auto& ab : _abilities.items()) { auto s = ab.value().get(); auto tryAbility = talentLibrary->TryGet(ArbUt::StringView(s.c_str(), s.length())); if (!tryAbility.has_value()) THROW("Unknown ability ", s); abilities.Append(tryAbility.value()); } auto hiddenAbilities = ArbUt::List>(_hiddenAbilities.size()); for (const auto& ab : _hiddenAbilities.items()) { auto s = ab.value().get(); auto tryAbility = talentLibrary->TryGet(ArbUt::StringView(s.c_str(), s.length())); if (!tryAbility.has_value()) THROW("Unknown ability ", s); hiddenAbilities.Append(tryAbility.value()); } auto moves = new PkmnLib::Library::LearnableMoves(100); auto& movesJson = forme["moves"]; auto& levelMovesJson = movesJson.at("levelMoves"); for (auto& levelMoveObj : levelMovesJson) { BuildLevelMove(levelMoveObj, moveLibrary, moves); } return new PkmnLib::Library::PokemonForme(ArbUt::StringView(name.c_str()), _height.get(), _weight.get(), _baseExp.get(), types, stats, abilities, hiddenAbilities, moves); } #undef GET