/** Load the research stats */ bool loadResearch(WzConfig &ini) { ASSERT(ini.isAtDocumentRoot(), "WzConfig instance is in the middle of traversal"); std::vector<WzString> list = ini.childGroups(); PLAYER_RESEARCH dummy; memset(&dummy, 0, sizeof(dummy)); std::vector< std::vector<WzString> > preResearch; preResearch.resize(list.size()); for (size_t inc = 0; inc < list.size(); ++inc) { // HACK FIXME: the code assumes we have empty PLAYER_RESEARCH entries to throw around for (auto &j : asPlayerResList) { j.push_back(dummy); } ini.beginGroup(list[inc]); RESEARCH research; research.index = inc; research.name = ini.string("name"); research.id = list[inc]; //check the name hasn't been used already ASSERT_OR_RETURN(false, checkResearchName(&research, inc), "Research name '%s' used already", getName(&research)); research.ref = REF_RESEARCH_START + inc; research.results = ini.json("results", nlohmann::json::array()); //set subGroup icon WzString subGroup = ini.value("subgroupIconID", "").toWzString(); if (subGroup.compare("") != 0) { research.subGroup = setIconID(subGroup.toUtf8().c_str(), getName(&research)); } else { research.subGroup = NO_RESEARCH_ICON; } //set key topic unsigned int keyTopic = ini.value("keyTopic", 0).toUInt(); ASSERT(keyTopic <= 1, "Invalid keyTopic for research topic - '%s' ", getName(&research)); if (keyTopic <= 1) { research.keyTopic = ini.value("keyTopic", 0).toUInt(); } else { research.keyTopic = 0; } //set tech code UBYTE techCode = ini.value("techCode", 0).toUInt(); ASSERT(techCode <= 1, "Invalid tech code for research topic - '%s' ", getName(&research)); if (techCode == 0) { research.techCode = TC_MAJOR; } else { research.techCode = TC_MINOR; } //set the iconID WzString iconID = ini.value("iconID", "").toWzString(); if (iconID.compare("") != 0) { research.iconID = setIconID(iconID.toUtf8().c_str(), getName(&research)); } else { research.iconID = NO_RESEARCH_ICON; } //get the IMDs used in the interface WzString statID = ini.value("statID", "").toWzString(); research.psStat = nullptr; if (statID.compare("") != 0) { //try find the structure stat with given name research.psStat = getCompStatsFromName(statID); ASSERT_OR_RETURN(false, research.psStat, "Could not find stats for %s research %s", statID.toUtf8().c_str(), getName(&research)); } WzString imdName = ini.value("imdName", "").toWzString(); if (imdName.compare("") != 0) { research.pIMD = modelGet(imdName); ASSERT(research.pIMD != nullptr, "Cannot find the research PIE '%s' for record '%s'", imdName.toUtf8().data(), getName(&research)); } WzString imdName2 = ini.value("imdName2", "").toWzString(); if (imdName2.compare("") != 0) { research.pIMD2 = modelGet(imdName2); ASSERT(research.pIMD2 != nullptr, "Cannot find the 2nd research '%s' PIE for record '%s'", imdName2.toUtf8().data(), getName(&research)); } WzString msgName = ini.value("msgName", "").toWzString(); if (msgName.compare("") != 0) { //check its a major tech code ASSERT(research.techCode == TC_MAJOR, "This research should not have a message associated with it, '%s' the message will be ignored!", getName(&research)); if (research.techCode == TC_MAJOR) { research.pViewData = getViewData(msgName); } } //set the researchPoints unsigned int resPoints = ini.value("researchPoints", 0).toUInt(); ASSERT_OR_RETURN(false, resPoints <= UWORD_MAX, "Research Points too high for research topic - '%s' ", getName(&research)); research.researchPoints = resPoints; //set the research power unsigned int resPower = ini.value("researchPower", 0).toUInt(); ASSERT_OR_RETURN(false, resPower <= UWORD_MAX, "Research Power too high for research topic - '%s' ", getName(&research)); research.researchPower = resPower; //remember research pre-requisites for futher checking preResearch[inc] = ini.value("requiredResearch").toWzStringList(); //set components results std::vector<WzString> compResults = ini.value("resultComponents").toWzStringList(); for (size_t j = 0; j < compResults.size(); j++) { WzString compID = compResults[j].trimmed(); COMPONENT_STATS *pComp = getCompStatsFromName(compID); if (pComp != nullptr) { research.componentResults.push_back(pComp); } else { ASSERT(false, "Invalid item '%s' in list of result components of research '%s' ", compID.toUtf8().c_str(), getName(&research)); } } //set replaced components std::vector<WzString> replacedComp = ini.value("replacedComponents").toWzStringList(); for (size_t j = 0; j < replacedComp.size(); j++) { //read pair of components oldComponent:newComponent std::vector<WzString> pair = replacedComp[j].split(":"); ASSERT(pair.size() == 2, "Invalid item '%s' in list of replaced components of research '%s'. Required format: 'oldItem:newItem, item1:item2'", replacedComp[j].toUtf8().c_str(), getName(&research)); if (pair.size() != 2) { continue; //skip invalid entries } WzString oldCompID = pair[0].trimmed(); WzString newCompID = pair[1].trimmed(); COMPONENT_STATS *oldComp = getCompStatsFromName(oldCompID); if (oldComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of replaced components of research '%s'. Wrong component code.", oldCompID.toUtf8().c_str(), getName(&research)); continue; } COMPONENT_STATS *newComp = getCompStatsFromName(newCompID); if (newComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of replaced components of research '%s'. Wrong component code.", newCompID.toUtf8().c_str(), getName(&research)); continue; } RES_COMP_REPLACEMENT replItem; replItem.pOldComponent = oldComp; replItem.pNewComponent = newComp; research.componentReplacement.push_back(replItem); } //set redundant components std::vector<WzString> redComp = ini.value("redComponents").toWzStringList(); for (size_t j = 0; j < redComp.size(); j++) { WzString compID = redComp[j].trimmed(); COMPONENT_STATS *pComp = getCompStatsFromName(compID); if (pComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of redundant components of research '%s' ", compID.toUtf8().c_str(), getName(&research)); } else { research.pRedArtefacts.push_back(pComp); } } //set result structures std::vector<WzString> resStruct = ini.value("resultStructures").toWzStringList(); for (size_t j = 0; j < resStruct.size(); j++) { WzString strucID = resStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID); ASSERT(structIndex >= 0, "Invalid item '%s' in list of result structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pStructureResults.push_back(structIndex); } } //set required structures std::vector<WzString> reqStruct = ini.value("requiredStructures").toWzStringList(); for (size_t j = 0; j < reqStruct.size(); j++) { WzString strucID = reqStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID.toUtf8().c_str()); ASSERT(structIndex >= 0, "Invalid item '%s' in list of required structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pStructList.push_back(structIndex); } } //set redundant structures std::vector<WzString> redStruct = ini.value("redStructures").toWzStringList(); for (size_t j = 0; j < redStruct.size(); j++) { WzString strucID = redStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID.toUtf8().c_str()); ASSERT(structIndex >= 0, "Invalid item '%s' in list of redundant structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pRedStructs.push_back(structIndex); } } asResearch.push_back(research); ini.endGroup(); } //Load and check research pre-requisites (need do it AFTER loading research items) for (size_t inc = 0; inc < asResearch.size(); inc++) { std::vector<WzString> &preRes = preResearch[inc]; for (size_t j = 0; j < preRes.size(); j++) { WzString resID = preRes[j].trimmed(); RESEARCH *preResItem = getResearch(resID.toUtf8().c_str()); ASSERT(preResItem != nullptr, "Invalid item '%s' in list of pre-requisites of research '%s' ", resID.toUtf8().c_str(), getName(&asResearch[inc])); if (preResItem != nullptr) { asResearch[inc].pPRList.push_back(preResItem->index); } } } return true; }