📄 unittable.cpp
字号:
// Based on Submarine's BuildTable Class from AAI. Thanks sub!
#include "UnitTable.h"
/// CR_BIND(CUnitTable, );
/// CR_REG_METADATA(CUnitTable, (
/// CR_MEMBER(all_lists),
/// CR_MEMBER(ground_factories),
/// CR_MEMBER(ground_builders),
/// CR_MEMBER(ground_attackers),
/// CR_MEMBER(metal_extractors),
/// CR_MEMBER(metal_makers),
/// CR_MEMBER(ground_energy),
/// CR_MEMBER(ground_defences),
/// CR_MEMBER(metal_storages),
/// CR_MEMBER(energy_storages),
/// CR_MEMBER(nuke_silos),
///
/// CR_MEMBER(numOfSides),
/// CR_MEMBER(sideNames),
/// CR_MEMBER(modSideMap),
/// CR_MEMBER(teamSides),
///
/// CR_MEMBER(unitTypes)
/// ));
CUnitTable::CUnitTable(AIClasses* ai) {
this->ai = ai;
numOfUnits = 0;
unitList = 0;
BuildModSideMap();
ReadTeamSides();
// now set up the unit lists
ground_factories = new vector<int>[numOfSides];
ground_builders = new vector<int>[numOfSides];
ground_attackers = new vector<int>[numOfSides];
metal_extractors = new vector<int>[numOfSides];
metal_makers = new vector<int>[numOfSides];
ground_energy = new vector<int>[numOfSides];
ground_defences = new vector<int>[numOfSides];
metal_storages = new vector<int>[numOfSides];
energy_storages = new vector<int>[numOfSides];
nuke_silos = new vector<int>[numOfSides];
all_lists.push_back(ground_factories);
all_lists.push_back(ground_builders);
all_lists.push_back(ground_attackers);
all_lists.push_back(metal_extractors);
all_lists.push_back(metal_makers);
all_lists.push_back(ground_energy);
all_lists.push_back(ground_defences);
all_lists.push_back(metal_storages);
all_lists.push_back(energy_storages);
all_lists.push_back(nuke_silos);
}
CUnitTable::~CUnitTable() {
delete[] unitTypes;
delete[] unitList;
delete[] ground_factories;
delete[] ground_builders;
delete[] ground_attackers;
delete[] metal_extractors;
delete[] metal_makers;
delete[] ground_energy;
delete[] ground_defences;
delete[] metal_storages;
delete[] energy_storages;
delete[] nuke_silos;
}
int CUnitTable::BuildModSideMap() {
// get all sides and commanders
std::string commKey; // eg. "SIDE4\\commander"
std::string commName; // eg. "arm_commander"
std::string sideKey; // eg. "SIDE4\\name"
std::string sideName; // eg. "Arm"
char sideNum[64] = {0};
ai->parser->LoadVirtualFile("gamedata\\SIDEDATA.tdf");
// look at SIDE0 through SIDE9
// (should be enough for any mod)
for (int i = 0; i < 10; i++) {
sprintf(sideNum, "%i", i);
commKey = "SIDE" + std::string(sideNum) + "\\commander";
sideKey = "SIDE" + std::string(sideNum) + "\\name";
ai->parser->GetDef(commName, "-1", commKey);
const UnitDef* udef = ai->cb->GetUnitDef(commName.c_str());
if (udef) {
// if this unit exists, the side is valid too
startUnits.push_back(udef->id);
ai->parser->GetDef(sideName, "-1", sideKey);
sideNames.push_back(sideName);
modSideMap[sideName] = i;
numOfSides = i + 1;
}
}
return numOfSides;
}
int CUnitTable::ReadTeamSides() {
char scriptFileName[1024] = {0};
ai->cb->GetValue(AIVAL_SCRIPT_FILENAME_CSTR, scriptFileName);
teamSides.resize(MAX_TEAMS, 0);
teamSides[0] = 0; // team 0 defaults to side 0
teamSides[1] = 1; // team 1 defaults to side 1
if (scriptFileName[0] > 0) {
// got a GameSetup script
char sideKey[128] = {0};
char sideName[128] = {0};
CSunParser scriptFileParser(ai);
scriptFileParser.LoadRealFile(scriptFileName);
for (int i = 0; i < MAX_TEAMS; i++) {
sideName[0] = 0;
snprintf(sideKey, 127, "GAME\\TEAM%d\\Side", i);
scriptFileParser.GetDef(sideName, "", sideKey);
if (sideName[0] > 0) {
// FIXME: Gaia-team side?
teamSides[i] = modSideMap[std::string(sideName)];
}
}
return teamSides[ai->cb->GetMyTeam()];
} else {
return teamSides[1];
}
}
int CUnitTable::GetSide(int unitID) {
int team = ai->cb->GetUnitTeam(unitID);
int side = teamSides[team];
return side;
}
int CUnitTable::GetCategory(const UnitDef* unitdef) {
return unitTypes[unitdef->id].category;
}
int CUnitTable::GetCategory(int unit) {
assert(ai->cb->GetUnitDef(unit) != NULL);
return (unitTypes[ai->cb->GetUnitDef(unit)->id].category);
}
// used to update threat-map
float CUnitTable::GetDPS(const UnitDef* unit) {
if (unit) {
float totaldps = 0.0f;
for (vector<UnitDef::UnitDefWeapon>::const_iterator i = unit->weapons.begin(); i != unit->weapons.end(); i++) {
float dps = 0.0f;
if (!i->def->paralyzer) {
int numberofdamages;
ai->cb->GetValue(AIVAL_NUMDAMAGETYPES, &numberofdamages);
float reloadtime = i->def->reload;
for (int k = 0; k < numberofdamages; k++) {
dps += i->def->damages[k];
}
dps = dps * i->def->salvosize / numberofdamages / reloadtime;
}
totaldps += dps;
}
return totaldps;
}
return 0.0f;
}
float CUnitTable::GetDPSvsUnit(const UnitDef* unit, const UnitDef* victim) {
if (unit->weapons.size()) {
ai->math->TimerStart();
float dps = 0.0f;
bool canhit = false;
string targetcat;
int armortype = victim->armorType;
int numberofdamages;
ai->cb->GetValue(AIVAL_NUMDAMAGETYPES, &numberofdamages);
for (unsigned int i = 0; i != unit->weapons.size(); i++) {
if (!unit->weapons[i].def->paralyzer) {
targetcat = unitTypes[unit->id].TargetCategories[i];
unsigned int a = victim->category;
unsigned int b = unit->weapons[i].def->onlyTargetCategory; // what the weapon can target
unsigned int c = unit->weapons[i].onlyTargetCat; // what the unit accepts as this weapons target
// unsigned int d = unit->weapons[i].badTargetCat; // what the unit thinks this weapon must be used for (hmm ?)
bool canWeaponTarget = (a & b) > 0;
bool canUnitTarget = (a & c) > 0; // how is this used?
// bool badUnitTarget = (a & d) > 0; // probably means that it has low priority
canhit = (canWeaponTarget && canUnitTarget);
if (!unit->weapons[i].def->waterweapon && ai->cb->GetUnitDefHeight(victim->id) - victim->waterline < 0) {
// weapon cannot hit this sub
canhit = false;
}
if (unit->weapons[i].def->waterweapon && victim->minWaterDepth == 0) {
// anti-sub weapon cannot kill this unit
canhit = false;
}
// bombers are useless against air
if (unit->weapons[i].def->dropped && victim->canfly && unit->canfly && unit->wantedHeight <= victim->wantedHeight) {
canhit = false;
}
if (canhit) {
float accuracy = unit->weapons[i].def->accuracy * 2.8;
if (victim->speed != 0) {
accuracy *= 1 - (unit->weapons[i].def->targetMoveError);
}
float basedamage = unit->weapons[i].def->damages[armortype] * unit->weapons[i].def->salvosize / unit->weapons[i].def->reload;
float AOE = unit->weapons[i].def->areaOfEffect * 0.7;
float tohitprobability = 0.0f;
float impactarea = 0.0f;
float targetarea = 0.0f;
float distancetravelled = 0.7f * unit->weapons[i].def->range;
float firingangle = 0.0f;
float gravity = -(ai->cb->GetGravity() * 900);
float timetoarrive = 0.0f;
float u = unit->weapons[i].def->projectilespeed * 30;
if (unit->weapons[i].def->type == string("Cannon")) {
float sinoid = (distancetravelled * gravity) / (u * u);
sinoid = min(sinoid, 1.0f);
firingangle = asin(sinoid) / 2;
if (unit->highTrajectoryType == 1) {
firingangle = (PI / 2) - firingangle;
}
float heightreached = pow(u * sin(firingangle), 2) / (2 * gravity);
float halfd = distancetravelled / 2;
distancetravelled = 2 * sqrt(halfd * halfd + heightreached * heightreached) * 1.1;
}
if ((victim->canfly && unit->weapons[i].def->selfExplode) || !victim->canfly) {
impactarea = pow((accuracy * distancetravelled) + AOE, 2);
targetarea = ((victim->xsize * 16) + AOE) * ((victim->ysize * 16) + AOE);
} else {
impactarea = pow((accuracy) * (0.7f * distancetravelled), 2);
targetarea = (victim->xsize * victim->ysize * 256);
}
if (impactarea > targetarea) {
tohitprobability = targetarea / impactarea;
} else {
tohitprobability = 1;
}
if (!unit->weapons[i].def->guided && unit->weapons[i].def->projectilespeed != 0 && victim->speed != 0 && unit->weapons[i].def->beamtime == 1) {
if (unit->weapons[i].def->type == string("Cannon")) {
timetoarrive = (2 * u * sin(firingangle)) / gravity;
} else {
timetoarrive = distancetravelled / (unit->weapons[i].def->projectilespeed * 30);
}
float shotwindow = sqrt(targetarea) / victim->speed * 1.3;
if (shotwindow < timetoarrive) {
tohitprobability *= shotwindow / timetoarrive;
}
}
dps += basedamage * tohitprobability;
}
}
}
return dps;
}
return 0.0f;
}
float CUnitTable::GetCurrentDamageScore(const UnitDef* unit) {
int enemies[MAX_UNITS];
int numEnemies = ai->cheat->GetEnemyUnits(enemies);
vector<int> enemiesOfType;
float score = 0.01f;
float totalCost = 0.01f;
enemiesOfType.resize(ai->cb->GetNumUnitDefs() + 1, 0);
for (int i = 0; i < numEnemies; i++) {
const UnitDef* udef = ai->cheat->GetUnitDef(enemies[i]);
if (udef) {
enemiesOfType[udef->id]++;
}
}
for (unsigned int i = 1; i < enemiesOfType.size(); i++) {
bool b1 = unitTypes[i].def->builder;
bool b2 = (enemiesOfType[i] > 0);
bool b3 = (unitTypes[i].sides.size() > 0);
// bool b4 = (!unit->speed && !unitTypes[i].def->speed);
if (!b1 && b2 && b3 /* && !b4 */) {
float currentScore = 0.0f;
float costOfEnemiesOfThisType = ((unitTypes[i].def->metalCost * METAL2ENERGY) + unitTypes[i].def->energyCost) * enemiesOfType[i];
currentScore = unitTypes[unit->id].DPSvsUnit[i] * costOfEnemiesOfThisType;
totalCost += costOfEnemiesOfThisType;
/*
if (unitTypes[i].DPSvsUnit[unit->id] * costofenemiesofthistype > 0) {
currentscore -= (unitTypes[i].DPSvsUnit[unit->id] * costofenemiesofthistype);
}
*/
score += currentScore;
}
}
if (totalCost <= 0)
return 0.0f;
return (score / totalCost);
}
void CUnitTable::UpdateChokePointArray() {
vector<float> EnemyCostsByMoveType;
EnemyCostsByMoveType.resize(ai->pather->NumOfMoveTypes);
vector<int> enemiesOfType;
float totalCost = 1.0f;
int enemies[MAX_UNITS];
int numEnemies = ai->cheat->GetEnemyUnits(enemies);
enemiesOfType.resize(ai->cb->GetNumUnitDefs() + 1, 0);
for (int i = 0; i < ai->pather->totalcells; i++) {
ai->dm->ChokePointArray[i] = 0;
}
for (int i = 0; i < ai->pather->NumOfMoveTypes; i++) {
EnemyCostsByMoveType[i] = 0;
}
for (int i = 0; i < numEnemies; i++) {
enemiesOfType[ai->cheat->GetUnitDef(enemies[i])->id]++;
}
for (unsigned int i = 1; i < enemiesOfType.size(); i++) {
if (unitTypes[i].sides.size() > 0 && !unitTypes[i].def->canfly && unitTypes[i].def->speed > 0) {
float currentcosts = ((unitTypes[i].def->metalCost * METAL2ENERGY) + unitTypes[i].def->energyCost) * (enemiesOfType[i]);
EnemyCostsByMoveType[unitTypes[i].def->moveType] += currentcosts;
totalCost += currentcosts;
}
}
for (int i = 0; i < ai->pather->NumOfMoveTypes; i++) {
EnemyCostsByMoveType[i] /= totalCost;
for (int c = 0; c < ai->pather->totalcells; c++) {
ai->dm->ChokePointArray[c] += ai->dm->ChokeMapsByMovetype[i][c] * EnemyCostsByMoveType[i];
}
}
}
float CUnitTable::GetScore(const UnitDef* udef, int category) {
int m = (ai->uh->AllUnitsByType[udef->id]).size();
int n = udef->maxThisUnit;
if (m >= n) {
// if we've hit the build-limit for this
// type of unit, make sure GetUnitByScore()
// won't pick it for construction anyway
return 0.0f;
}
int frame = ai->cb->GetCurrentFrame();
float Cost = ((udef->metalCost * METAL2ENERGY) + udef->energyCost) + 0.1f;
float CurrentIncome = INCOMEMULTIPLIER * (ai->cb->GetEnergyIncome() + (ai->cb->GetMetalIncome() * METAL2ENERGY)) + frame / 2;
float Hitpoints = udef->health;
float buildTime = udef->buildTime + 0.1f;
float benefit = 0.0f;
float dps = 0.0f;
int unitcounter = 0;
bool candevelop = false;
float RandNum = ai->math->RandNormal(4, 3, 1) + 1;
float randMult = float((rand() % 2) + 1);
switch (category) {
case CAT_ENERGY: {
// KLOOTNOTE: factor build-time into this as well
// (so benefit values generally lie closer together)
// benefit = (udef->energyMake - udef->energyUpkeep);
// benefit = (udef->energyMake - udef->energyUpkeep) * randMult;
benefit = ((udef->energyMake - udef->energyUpkeep) / buildTime) * randMult;
if (udef->windGenerator) {
benefit += ai->cb->GetMinWind();
}
if (udef->tidalGenerator) {
benefit += ai->cb->GetTidalStrength();
}
// filter geothermals
if (udef->needGeo)
benefit = 0.0f;
// KLOOTNOTE: dividing by cost here as well means
// benefit is inversely proportional to square of
// cost, so expensive generators are quadratically
// less likely to be built if original calculation
// of score is used
// benefit /= Cost;
} break;
case CAT_MEX: {
benefit = pow(udef->extractsMetal, 4.0f);
} break;
case CAT_MMAKER: {
// benefit = ((udef->metalMake - udef->metalUpkeep) / udef->energyUpkeep) + 0.01f;
benefit = (udef->metalMake - udef->metalUpkeep) / (udef->energyUpkeep + 0.01f);
} break;
case CAT_G_ATTACK: {
dps = GetCurrentDamageScore(udef);
if (udef->canfly && !udef->hoverAttack) {
// TODO: improve to set reload-time to the bomber's
// turnspeed vs. movespeed (eg. time taken for a run)
dps /= 6;
}
benefit = pow((udef->weapons.front().def->areaOfEffect + 80), 1.5f)
* pow(GetMaxRange(udef) + 200, 1.5f)
* pow(dps, 1.0f)
* pow(udef->speed + 40, 1.0f)
* pow(Hitpoints, 1.0f)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -