void CUnitTable::Init() { numDefs = ai->cb->GetNumUnitDefs(); // one more than needed because [0] is a dummy object (so // UnitDef->id can be used to adress that unit in array) unitTypes.resize(numDefs + 1); unitDefs.resize(numDefs, NULL); ai->cb->GetUnitDefList(&unitDefs[0]); // add units to UnitTable for (int i = 1; i <= numDefs; i++) { unitTypes[i].def = unitDefs[i - 1]; unitTypes[i].category = CAT_LAST; // GetUnitDefList() filled our unitDefs // partially with null UnitDef*'s (bad, // nothing much to do if this happens) assert(unitTypes[i].def != 0x0); if ((unitTypes[i].def)->movedata != NULL) { moveDefs[(unitTypes[i].def)->movedata->pathType] = (unitTypes[i].def)->movedata; } std::map<int, std::string>::const_iterator j; // get build options for (j = unitTypes[i].def->buildOptions.begin(); j != unitTypes[i].def->buildOptions.end(); j++) { const char* buildOptName = j->second.c_str(); const UnitDef* buildOptDef = ai->cb->GetUnitDef(buildOptName); unitTypes[i].canBuildList.push_back(buildOptDef->id); } } // add unit to different groups for (int i = 1; i <= numDefs; i++) { UnitType* uType = &unitTypes[i]; const int UnitCost = int(uType->def->metalCost * METAL2ENERGY + uType->def->energyCost); if (/* uType->def->isCommander && */ uType->def->buildOptions.size() > 0) { uType->category = CAT_BUILDER; } uType->DPSvsUnit.resize(numDefs + 1); // calculate this unit type's DPS against all other unit types for (int v = 1; v <= numDefs; v++) { uType->DPSvsUnit[v] = GetDPSvsUnit(uType->def, unitTypes[v].def); } // speed > 0 means we are mobile, minWaterDepth <= 0 means we // are allergic to water and cannot be in it (positive values // are inverted internally) if (uType->def->speed > 0.0f /* && uType->def->minWaterDepth <= 0 */) { if (uType->def->buildOptions.size() > 0) { categoryData.groundBuilders.push_back(i); uType->category = CAT_BUILDER; } else if (!uType->def->weapons.empty() && !uType->def->weapons.begin()->def->stockpile) { categoryData.groundAttackers.push_back(i); uType->category = CAT_G_ATTACK; } } else if (!uType->def->canfly) { if (true /* uType->def->minWaterDepth <= 0 */) { if (uType->def->buildOptions.size() >= 1 && uType->def->builder) { if ((((uType->def)->TEDClassString) == "PLANT") || (((uType->def)->speed) > 0.0f)) { uType->isHub = false; } else { uType->isHub = true; } categoryData.groundFactories.push_back(i); uType->category = CAT_FACTORY; } else { const WeaponDef* weapon = (uType->def->weapons.empty())? 0: uType->def->weapons.begin()->def; if (weapon && !weapon->stockpile && uType->def->extractsMetal == 0.0f) { // we don't want armed extractors to be seen as general-purpose defense if (!weapon->waterweapon) { // filter out depth-charge launchers etc categoryData.groundDefenses.push_back(i); uType->category = CAT_DEFENCE; } } if (uType->def->stockpileWeaponDef) { if (uType->def->stockpileWeaponDef->targetable) { // nuke categoryData.nukeSilos.push_back(i); uType->category = CAT_NUKE; } if (uType->def->stockpileWeaponDef->interceptor) { // anti-nuke, not implemented yet } } if (uType->def->shieldWeaponDef && uType->def->shieldWeaponDef->isShield) { // shield, not implemented yet // uType->category = CAT_SHIELD; } if (uType->def->makesMetal) { categoryData.metalMakers.push_back(i); uType->category = CAT_MMAKER; } if (uType->def->extractsMetal > 0.0f) { categoryData.metalExtractors.push_back(i); uType->category = CAT_MEX; } if (((uType->def->energyMake - uType->def->energyUpkeep) / UnitCost) > 0.002 || uType->def->tidalGenerator || uType->def->windGenerator) { if (/* uType->def->minWaterDepth <= 0 && */ !uType->def->needGeo) { // filter tidals and geothermals categoryData.groundEnergy.push_back(i); uType->category = CAT_ENERGY; } } if (uType->def->energyStorage / UnitCost > 0.2) { categoryData.energyStorages.push_back(i); uType->category = CAT_ESTOR; } if (uType->def->metalStorage / UnitCost > 0.1) { categoryData.metalStorages.push_back(i); uType->category = CAT_MSTOR; } } } } } ReadModConfig(); // dump generated unit table to file DebugPrint(); }
void CUnitTable::Init() { // get the unitdefs and stick them in the unitTypes[] array numOfUnits = ai->cb->GetNumUnitDefs(); unitList = new const UnitDef*[numOfUnits]; ai->cb->GetUnitDefList(unitList); // one more than needed because [0] is a dummy object (so // UnitDef->id can be used to adress that unit in array) unitTypes = new UnitType[numOfUnits + 1]; // add units to UnitTable for (int i = 1; i <= numOfUnits; i++) { unitTypes[i].def = unitList[i - 1]; // side has not been assigned - will be done later unitTypes[i].category = -1; // GetUnitDefList() filled our unitList // partially with null UnitDef*'s (bad, // nothing much to do if this happens) assert(unitTypes[i].def != 0x0); // get build options for (map<int, string>::const_iterator j = unitTypes[i].def->buildOptions.begin(); j != unitTypes[i].def->buildOptions.end(); j++) { const char* buildOptionName = j->second.c_str(); const UnitDef* buildOptionDef = ai->cb->GetUnitDef(buildOptionName); unitTypes[i].canBuildList.push_back(buildOptionDef->id); } } // now set sides and create buildtree for each // note: this skips Lua commanders completely! for (int s = 0; s < numOfSides; s++) { // set side of start unit (eg. commander) and continue recursively int unitDefID = startUnits[s]; unitTypes[unitDefID].sides.insert(s); CalcBuildTree(unitDefID, s); } // add unit to different groups for (int i = 1; i <= numOfUnits; i++) { UnitType* me = &unitTypes[i]; // KLOOTNOTE: this is a hack to make KAIK recognize Lua // commanders ((which are unreachable from the starting // units in the mod hierarchy and so will be skipped by // CalcBuildTree(), meaning me->sides stays empty)) as // builders, but the ground_builders[side] list for this // unit might not exist (and will never actually contain // this unitDef ID) if (/* me->def->isCommander && */ me->def->buildOptions.size() > 0) { me->category = CAT_BUILDER; } for (std::set<int>::iterator it = me->sides.begin(); it != me->sides.end(); it++) { int mySide = *it; int UnitCost = int(me->def->metalCost * METAL2ENERGY + me->def->energyCost); me->TargetCategories.resize(me->def->weapons.size()); if (me->def->filename.find(".lua") != std::string::npos) { // can't parse these without a Lua parser for (unsigned int w = 0; w != me->def->weapons.size(); w++) { me->TargetCategories[w] = ""; } } else { CSunParser attackerParser(ai); attackerParser.LoadVirtualFile(me->def->filename.c_str()); for (unsigned int w = 0; w != me->def->weapons.size(); w++) { char weaponnumber[10] = ""; itoa(w, weaponnumber, 10); attackerParser.GetDef(me->TargetCategories[w], "-1", string("UNITINFO\\OnlyTargetCategory") + string(weaponnumber)); } } me->DPSvsUnit.resize(numOfUnits + 1); // calculate this unit type's DPS against all other unit types for (int v = 1; v <= numOfUnits; v++) { me->DPSvsUnit[v] = GetDPSvsUnit(me->def, unitTypes[v].def); } // speed > 0 means we are mobile, minWaterDepth <= 0 means we // are allergic to water and cannot be in it (positive values // are inverted internally) if (me->def->speed > 0.0f /* && me->def->minWaterDepth <= 0 */) { if (me->def->buildOptions.size() > 0) { ground_builders[mySide].push_back(i); me->category = CAT_BUILDER; } else if (!me->def->weapons.empty() && !me->def->weapons.begin()->def->stockpile) { ground_attackers[mySide].push_back(i); me->category = CAT_G_ATTACK; } } else if (!me->def->canfly) { if (true /* me->def->minWaterDepth <= 0 */) { if (me->def->buildOptions.size() >= 1 && me->def->builder) { if ((((me->def)->TEDClassString) == "PLANT") || (((me->def)->speed) > 0.0f)) { me->isHub = false; } else { me->isHub = true; } ground_factories[mySide].push_back(i); me->category = CAT_FACTORY; } else { const WeaponDef* weapon = (me->def->weapons.empty())? 0: me->def->weapons.begin()->def; if (weapon && !weapon->stockpile && me->def->extractsMetal == 0.0f) { // we don't want armed extractors to be seen as general-purpose defense if (!weapon->waterweapon) { // filter out depth-charge launchers etc ground_defences[mySide].push_back(i); me->category = CAT_DEFENCE; } } if (me->def->stockpileWeaponDef) { if (me->def->stockpileWeaponDef->targetable) { // nuke nuke_silos[mySide].push_back(i); me->category = CAT_NUKE; } if (me->def->stockpileWeaponDef->interceptor) { // anti-nuke, not implemented yet } } if (me->def->shieldWeaponDef && me->def->shieldWeaponDef->isShield) { // shield, not implemented yet // me->category = CAT_SHIELD; } if (me->def->makesMetal) { metal_makers[mySide].push_back(i); me->category = CAT_MMAKER; } if (me->def->extractsMetal > 0.0f) { metal_extractors[mySide].push_back(i); me->category = CAT_MEX; } if (((me->def->energyMake - me->def->energyUpkeep) / UnitCost) > 0.002 || me->def->tidalGenerator || me->def->windGenerator) { if (/* me->def->minWaterDepth <= 0 && */ !me->def->needGeo) { // filter tidals and geothermals ground_energy[mySide].push_back(i); me->category = CAT_ENERGY; } } if (me->def->energyStorage / UnitCost > 0.2) { energy_storages[mySide].push_back(i); me->category = CAT_ESTOR; } if (me->def->metalStorage / UnitCost > 0.1) { metal_storages[mySide].push_back(i); me->category = CAT_MSTOR; } } } } } } ReadModConfig(); // dump generated unit table to file DebugPrint(); }