📄 unittable.cpp
字号:
* pow(RandNum, 2.5f)
* pow(Cost, -0.5f);
if (udef->canfly || udef->canhover) {
// general hack: reduce feasibility of aircraft for 20 mins
// and that of hovercraft permanently, should mostly prefer
// real L2 units to hovers
benefit = (udef->canfly && frame >= (30 * 60 * 20))? benefit: benefit * 0.01f;
}
} break;
case CAT_DEFENCE: {
benefit = pow((udef->weapons.front().def->areaOfEffect + 80), 1.5f)
* pow(GetMaxRange(udef), 2.0f)
* pow(GetCurrentDamageScore(udef), 1.5f)
* pow(Hitpoints, 0.5f)
* pow(RandNum, 2.5f)
* pow(Cost, -1.0f);
} break;
case CAT_BUILDER: {
for (unsigned int i = 0; i != unitTypes[udef->id].canBuildList.size(); i++) {
if (unitTypes[unitTypes[udef->id].canBuildList[i]].category == CAT_FACTORY) {
candevelop = true;
}
}
// builder units that cannot construct any
// factories are worthless, prevent them
// from being chosen via GetUnitByScore()
// (they might have other uses though, eg.
// nano-towers)
if (!candevelop) {
benefit = 0.0f;
} else {
benefit = pow(udef->buildSpeed, 1.0f)
* pow(udef->speed, 0.5f)
* pow(Hitpoints, 0.3f)
* pow(RandNum, 0.4f);
}
} break;
case CAT_FACTORY: {
// benefit of a factory is dependant on the kind of
// offensive units it can build, but EE-hubs are only
// capable of building other buildings
for (unsigned int i = 0; i != unitTypes[udef->id].canBuildList.size(); i++) {
int buildOption = unitTypes[udef->id].canBuildList[i];
int buildOptionCategory = unitTypes[buildOption].category;
if (buildOptionCategory == CAT_G_ATTACK || buildOptionCategory == CAT_FACTORY) {
if (unitTypes[buildOption].def != udef) {
// KLOOTNOTE: guard against infinite recursion (BuildTowers in
// PURE trigger this since they are able to build themselves)
benefit += GetScore(unitTypes[buildOption].def, buildOptionCategory);
unitcounter++;
}
}
}
if (unitcounter > 0) {
benefit /= (unitcounter * pow(float(ai->uh->AllUnitsByType[udef->id].size() + 1), 3.0f));
} else {
benefit = 0.0f;
}
} break;
case CAT_MSTOR: {
benefit = pow((udef->metalStorage), 1.0f) * pow(Hitpoints, 1.0f);
} break;
case CAT_ESTOR: {
benefit = pow((udef->energyStorage), 1.0f) * pow(Hitpoints, 1.0f);
} break;
case CAT_NUKE: {
// KLOOTNOTE: should factor damage into this as well
float metalcost = udef->stockpileWeaponDef->metalcost;
float energycost = udef->stockpileWeaponDef->energycost;
float supplycost = udef->stockpileWeaponDef->supplycost;
float denom = metalcost + energycost + supplycost + 1.0f;
float range = udef->stockpileWeaponDef->range;
benefit = (udef->stockpileWeaponDef->areaOfEffect + range) / denom;
} break;
/*
case CAT_ANTINUKE: {
benefit = udef->stockpileWeaponDef->coverageRange;
} break;
case CAT_SHIELD: {
benefit = udef->shieldWeaponDef->shieldRadius;
} break;
*/
default:
benefit = 0.0f;
}
// return (benefit / (CurrentIncome + Cost));
// return ((benefit / Cost) * CurrentIncome);
return ((CurrentIncome / Cost) * benefit);
}
// operates in terms of GetScore()
const UnitDef* CUnitTable::GetUnitByScore(int builderUnitID, int category) {
vector<int>* tempList = 0;
const UnitDef* builderDef = ai->cb->GetUnitDef(builderUnitID);
const UnitDef* tempUnitDef = 0;
int side = GetSide(builderUnitID);
float tempScore = 0.0f;
float bestScore = 0.0f;
switch (category) {
case CAT_ENERGY:
tempList = ground_energy;
break;
case CAT_MEX:
tempList = metal_extractors;
break;
case CAT_MMAKER:
tempList = metal_makers;
break;
case CAT_G_ATTACK:
tempList = ground_attackers;
break;
case CAT_DEFENCE:
tempList = ground_defences;
break;
case CAT_BUILDER:
tempList = ground_builders;
break;
case CAT_FACTORY:
tempList = ground_factories;
break;
case CAT_MSTOR:
tempList = metal_storages;
break;
case CAT_ESTOR:
tempList = energy_storages;
break;
case CAT_NUKE:
tempList = nuke_silos;
break;
}
// iterate over all units for <side> in tempList (eg. Core ground_defences)
for (unsigned int i = 0; i != tempList[side].size(); i++) {
int tempUnitDefID = tempList[side][i];
// if our builder can build the i-th unit
if (CanBuildUnit(builderDef->id, tempUnitDefID)) {
// get the unit's heuristic score (based on current income)
tempScore = GetScore(unitTypes[tempUnitDefID].def, category);
if (tempScore > bestScore) {
bestScore = tempScore;
tempUnitDef = unitTypes[tempUnitDefID].def;
}
}
}
// if we didn't find a unit to build with score > 0 (ie.
// if builder has no build-option matching this category)
// then return NULL instead of first option on build menu
// (to prevent radar farms and other bizarro side-effects)
return ((bestScore > 0.0f)? tempUnitDef: NULL);
}
/*
* find and return the unit that's best to make for this builder
* (returns NULL if no buildings/units are better than minUsefulness
* or buidler can't make any economy units)
* TODO: make it, look at how to integrate into the main economy manager
*/
/*
const UnitDef* CUnitTable::GetBestEconomyBuilding(int builder, float minUsefulness) {
return 0;
}
*/
void CUnitTable::Init() {
// get unit defs from game 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 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;
// get build options
for (map<int, string>::const_iterator j = unitTypes[i].def->buildOptions.begin(); j != unitTypes[i].def->buildOptions.end(); j++) {
unitTypes[i].canBuildList.push_back(ai->cb->GetUnitDef(j->second.c_str())->id);
}
}
// now set sides and create buildtree for each
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);
unitTypes[unitDefID].techLevel = 0;
CalcBuildTree(unitDefID, s);
}
// add unit to different groups
for (int i = 1; i <= numOfUnits; i++) {
UnitType* me = &unitTypes[i];
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);
CSunParser attackerParser(ai);
char weaponnumber[10] = "";
attackerParser.LoadVirtualFile(me->def->filename.c_str());
me->TargetCategories.resize(me->def->weapons.size());
for (unsigned int w = 0; w != me->def->weapons.size(); w++) {
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);
}
if (me->def->speed && me->def->minWaterDepth <= 0) {
if (me->def->buildOptions.size()) {
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 (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) {
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;
}
}
}
}
}
}
// dump generated unit table to file
DebugPrint();
}
bool CUnitTable::CanBuildUnit(int id_builder, int id_unit) {
// look in build options of builder for unit
for (unsigned int i = 0; i != unitTypes[id_builder].canBuildList.size(); i++) {
if (unitTypes[id_builder].canBuildList[i] == id_unit)
return true;
}
// unit not found in builder's buildoptions
return false;
}
// determines sides of unitTypes by recursion
void CUnitTable::CalcBuildTree(int unitDefID, int rootSide) {
UnitType* utype = &unitTypes[unitDefID];
// go through all possible build options and set side if necessary
for (unsigned int i = 0; i != utype->canBuildList.size(); i++) {
// add this unit to target's built-by list
int buildOptionIndex = utype->canBuildList[i];
UnitType* buildOptionType = &unitTypes[buildOptionIndex];
// KLOOTNOTE: techLevel will not make much sense if
// unit has multiple ancestors at different depths
// in tree (eg. Adv. Vehicle Plants in XTA)
buildOptionType->techLevel = utype->techLevel;
// buildOptionType->techLevel = utype->def->techLevel;
// FIXME: causes duplicated entries in PURE
// buildOptionType->builtByList.push_back(unitDefID);
if (buildOptionType->sides.find(rootSide) == buildOptionType->sides.end()) {
// unit has not been checked yet, set side
// as side of its builder and continue
buildOptionType->sides.insert(rootSide);
CalcBuildTree(buildOptionIndex, rootSide);
}
}
}
void CUnitTable::DebugPrint() {
if (!unitList)
return;
// NOTE: same order as all_lists, not as CAT_* enum
const char* listCategoryNames[12] = {
"GROUND-FACTORY", "GROUND-BUILDER", "GROUND-ATTACKER", "METAL-EXTRACTOR",
"METAL-MAKER", "GROUND-ENERGY", "GROUND-DEFENSE", "METAL-STORAGE",
"ENERGY-STORAGE", "NUKE-SILO", "SHIELD-GENERATOR", "LAST-CATEGORY"
};
char filename[1024] = ROOTFOLDER"CUnitTable.log";
ai->cb->GetValue(AIVAL_LOCATE_FILE_W, filename);
FILE* file = fopen(filename, "w");
for (int i = 1; i <= numOfUnits; i++) {
UnitType* utype = &unitTypes[i];
fprintf(file, "UnitDef ID: %i\n", i);
fprintf(file, "Name: %s\n", unitList[i - 1]->humanName.c_str());
fprintf(file, "Sides: ");
for (std::set<int>::iterator it = utype->sides.begin(); it != utype->sides.end(); it++) {
fprintf(file, "%d (%s) ", *it, sideNames[*it].c_str());
}
fprintf(file, "\n");
fprintf(file, "Can Build: ");
for (unsigned int j = 0; j != utype->canBuildList.size(); j++) {
UnitType* buildOption = &unitTypes[utype->canBuildList[j]];
for (std::set<int>::iterator it = buildOption->sides.begin(); it != buildOption->sides.end(); it++) {
const char* sideName = sideNames[*it].c_str();
const char* buildOptionName = buildOption->def->humanName.c_str();
fprintf(file, "'(%s) %s' ", sideName, buildOptionName);
}
}
fprintf(file, "\n");
fprintf(file, "Built by: ");
for (unsigned int k = 0; k != utype->builtByList.size(); k++) {
UnitType* parent = &unitTypes[utype->builtByList[k]];
for (std::set<int>::iterator it = parent->sides.begin(); it != parent->sides.end(); it++) {
const char* sideName = sideNames[*it].c_str();
const char* parentName = parent->def->humanName.c_str();
fprintf(file, "'(%s) %s' ", sideName, parentName);
}
}
fprintf(file, "\nTech-Level: %d", utype->techLevel);
fprintf(file, "\n\n");
}
for (int s = 0; s < numOfSides; s++) {
for (unsigned int l = 0; l != all_lists.size(); l++) {
fprintf(file, "\n\n%s (side %d) units of category %s:\n", sideNames[s].c_str(), s, listCategoryNames[l]);
for (unsigned int i = 0; i != all_lists[l][s].size(); i++)
fprintf(file, "\t%s\n", unitTypes[all_lists[l][s][i]].def->humanName.c_str());
}
}
fclose(file);
}
float CUnitTable::GetMaxRange(const UnitDef* unit) {
float max_range = 0.0f;
for (vector<UnitDef::UnitDefWeapon>::const_iterator i = unit->weapons.begin(); i != unit->weapons.end(); i++) {
if ((i->def->range) > max_range)
max_range = i->def->range;
}
return max_range;
}
float CUnitTable::GetMinRange(const UnitDef* unit) {
float min_range = FLT_MAX;
for (vector<UnitDef::UnitDefWeapon>::const_iterator i = unit->weapons.begin(); i != unit->weapons.end(); i++) {
if ((i->def->range) < min_range)
min_range = i->def->range;
}
return min_range;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -