#include "TechTreeImproved.h" using json = nlohmann::json; TechTreeImproved::TechTreeImproved() { } sc2::UNIT_TYPEID string_to_id(const std::string & str) { return static_cast<sc2::UNIT_TYPEID>(std::stoi(str)); } std::string id_to_string(int id) { return sc2::UnitTypeToName(static_cast<sc2::UNIT_TYPEID>(id)); } void add_requirement(ResearchDescription & description, json & requirement) { std::string type = requirement["type"]; if (type == "and") { for (auto & subrequirement : requirement["operands"]) { add_requirement(description, subrequirement); } } else if (type == "not") { // Ignore this. This is used for: "We cannot do upgrade X if upgrade X is already running somewhere." } else if (type == "unitCount") { if (requirement["state"] == "CompleteOnly") { for (auto & unit : requirement["unit"]) { //std::cout << "Just building: " << sc2::UnitTypeToName(static_cast<sc2::UNIT_TYPEID>(unit)) << " (" << unit << ")" << std::endl; description.buildings_needed.push_back(static_cast<sc2::UNIT_TYPEID>(unit)); } } else { std::cout << "Unexpected state: " << requirement["state"] << std::endl; } } else if (type == "eq") { // TODO: Should we be more careful here? sc2::UPGRADE_ID id = static_cast<sc2::UPGRADE_ID>(requirement["operands"][0]["upgrade"]); int count = static_cast<int>(requirement["operands"][1]["value"]); if (count == 1) { description.upgrades_needed.push_back(id); } } } void add_requirement(BuildDescription & description, json & requirement) { std::string type = requirement["type"]; if (type == "and") { for (auto & subrequirement : requirement["operands"]) { add_requirement(description, subrequirement); } } else if (type == "unitCount") { // Units that needs to be completed at the unit location -> Addons if (requirement["state"] == "CompleteOnlyAtUnit") { for (auto & unit : requirement["unit"]) { //std::cout << "Addon: " << sc2::UnitTypeToName(static_cast<sc2::UNIT_TYPEID>(unit)) << " (" << unit << ")" << std::endl; sc2::UNIT_TYPEID unit_typeid = static_cast<sc2::UNIT_TYPEID>(unit); if (unit_typeid == sc2::UNIT_TYPEID::TERRAN_TECHLAB) { switch (description.producer_type) { case sc2::UNIT_TYPEID::TERRAN_BARRACKS: unit_typeid = sc2::UNIT_TYPEID::TERRAN_BARRACKSTECHLAB; break; case sc2::UNIT_TYPEID::TERRAN_FACTORY: unit_typeid = sc2::UNIT_TYPEID::TERRAN_FACTORYTECHLAB; break; case sc2::UNIT_TYPEID::TERRAN_STARPORT: unit_typeid = sc2::UNIT_TYPEID::TERRAN_STARPORTTECHLAB; break; default: std::wcout << "Unknown producer type for TECHLAB addon: " << sc2::UnitTypeToName(unit_typeid) << std::endl; } } description.addons_needed.push_back(unit_typeid); } } else if (requirement["state"] == "CompleteOnly") { for (auto & unit : requirement["unit"]) { //std::cout << "Just building: " << sc2::UnitTypeToName(static_cast<sc2::UNIT_TYPEID>(unit)) << " (" << unit << ")" << std::endl; description.buildings_needed.push_back(static_cast<sc2::UNIT_TYPEID>(unit)); } } else { //std::cout << "Unsupported" << std::endl; //std::cout << requirement << std::endl; } } } void parse_build_description(BuildDescription & description, json & build_item) { description.result_type = static_cast<sc2::UNIT_TYPEID>(build_item["unit"]); description.build_ability = static_cast<sc2::ABILITY_ID>(build_item["ability"]); description.buildTime = static_cast<int>(build_item["time"]); // The time in seconds it takes to create the unit parsed from the json file if (build_item.find("requires") != build_item.end()) { //std::cout << "Building unit: " << id_to_string(build_item["unit"]) << " requires" << std::endl; auto & requires = build_item["requires"][0]; add_requirement(description, requires); } } void parse_research_item(ResearchDescription & description, json & research_item) { //std::cout << "Parsing " << research_item["upgradeName"] << std::endl; description.result_type = static_cast<sc2::UPGRADE_ID>(research_item["upgrade"]); description.ability_used = static_cast<sc2::ABILITY_ID>(research_item["ability"]); if (research_item.find("requires") != research_item.end() && research_item["requires"].size() > 0) { auto & requires = research_item["requires"][0]; add_requirement(description, requires); } } void TechTreeImproved::parse_unit(json::iterator it) { sc2::UNIT_TYPEID producer_id = string_to_id(it.key()); //std::string name = sc2::UnitTypeToName(producer_id); if (it.value().find("builds") != it.value().end()) { auto & builds = it.value()["builds"]; for (auto & build_item : builds) { BuildDescription description; description.producer_type = producer_id; parse_build_description(description, build_item); if (result_to_data.count(description.result_type) > 0) { //std::cout << "Found more than one way to build " << sc2::UnitTypeToName(description.result_type) << " (" << (unsigned int) description.result_type << ")" << std::endl; result_to_data[description.result_type].push_back(description); } else { result_to_data[description.result_type] = { description }; } build_descriptions.push_back(description); } } // TODO: Use the result from the call to find for actually looking up data later, instead of searching twice if (it.value().find("researches") != it.value().end()) { //std::cout << "Found upgrades on unit " << it.value()["name"] << std::endl; for (auto & research_item : it.value()["researches"]) { ResearchDescription description; description.producer_type = producer_id; parse_research_item(description, research_item); if (upgrade_to_data.count(description.result_type)) { upgrade_to_data[description.result_type].push_back(description); } else { upgrade_to_data[description.result_type] = { description }; } research_descriptions.push_back(description); } } if (it.value().find("morphs") != it.value().end()) { for (auto & morph_item : it.value()["morphs"]) { BuildDescription description; description.producer_type = producer_id; description.morph_ability = static_cast<sc2::ABILITY_ID>(morph_item["ability"]); description.result_type = static_cast<sc2::UNIT_TYPEID>(morph_item["unit"]); if (morph_item.find("requires") != morph_item.end()) { for (auto & requirement : morph_item["requires"]) { std::string type = requirement["type"]; if (type == "unitCount") { for (auto & unit : requirement["unit"]) { sc2::UNIT_TYPEID unit_typeid = static_cast<sc2::UNIT_TYPEID>(unit); description.buildings_needed.push_back(unit_typeid); } } else if (type == "upgradeCount") { sc2::UPGRADE_ID upgrade_id = static_cast<sc2::UPGRADE_ID>(requirement["upgrade"]); description.upgrades_needed.push_back(upgrade_id); } } } build_descriptions.push_back(description); } } } bool TechTreeImproved::LoadData() { std::ifstream i("techtree.json"); if (!i.good()) { std::wcerr << "File techtree.json cannot be found, information regarding addons and required buildings will not be up to date. Please put techtree.json in working directory." << std::endl; return false; } // Parse the file's content json j; i >> j; // Time to parse content of the JSON file for (auto & race : j) { for (json::iterator it = race.begin(); it != race.end(); ++it) { parse_unit(it); } } return true; } const std::vector<BuildDescription> & TechTreeImproved::HowToBuild(sc2::UnitTypeID unit) const { if (result_to_data.count(unit) > 0) { return result_to_data.at(unit); } else { std::cout << "No information about unit type " << sc2::UnitTypeToName(unit) << " (" << static_cast<int>(unit) << ")" << std::endl; return empty_build; } } const std::vector<ResearchDescription> & TechTreeImproved::HowToResearch(sc2::UpgradeID upgrade) const { if (upgrade_to_data.count(upgrade)) { return upgrade_to_data.at(upgrade); } else { std::cout << "No information about upgrade type " << sc2::UpgradeIDToName(upgrade) << " (" << static_cast<int>(upgrade) << ")" << std::endl; return empty_research; } }