#include "CUnitTable.h"
#include
#include
#include
#include "CAI.h"
#include "CUnit.h"
#include "CConfigParser.h"
#include "Util.hpp"
#include "ReusableObjectFactory.hpp"
std::map CUnitTable::str2cat;
CUnitTable::UnitCategory2StrMap CUnitTable::cat2str;
std::vector CUnitTable::cats;
CUnitTable::CUnitTable(AIClasses *ai): ARegistrar(100) {
this->ai = ai;
if (cat2str.empty()) {
/* techlevels */
cat2str[TECH1] = "TECH1";
cat2str[TECH2] = "TECH2";
cat2str[TECH3] = "TECH3";
cat2str[TECH4] = "TECH4";
cat2str[TECH5] = "TECH5";
/* main categories */
cat2str[AIR] = "AIR";
cat2str[SEA] = "SEA";
cat2str[LAND] = "LAND";
cat2str[SUB] = "SUB";
cat2str[STATIC] = "STATIC";
cat2str[MOBILE] = "MOBILE";
/* builders */
cat2str[FACTORY] = "FACTORY";
cat2str[BUILDER] = "BUILDER";
cat2str[ASSISTER] = "ASSISTER";
cat2str[RESURRECTOR] = "RESURRECTOR";
/* offensives */
cat2str[COMMANDER] = "COMMANDER";
cat2str[ATTACKER] = "ATTACKER";
cat2str[ANTIAIR] = "ANTIAIR";
cat2str[SCOUTER] = "SCOUTER";
cat2str[ARTILLERY] = "ARTILLERY";
cat2str[SNIPER] = "SNIPER";
cat2str[ASSAULT] = "ASSAULT";
/* economic */
cat2str[MEXTRACTOR] = "MEXTRACTOR";
cat2str[MMAKER] = "MMAKER";
cat2str[EMAKER] = "EMAKER";
cat2str[MSTORAGE] = "MSTORAGE";
cat2str[ESTORAGE] = "ESTORAGE";
/* factory types */
cat2str[KBOT] = "KBOT";
cat2str[VEHICLE] = "VEHICLE";
cat2str[HOVER] = "HOVER";
cat2str[AIRCRAFT] = "AIRCRAFT";
cat2str[NAVAL] = "NAVAL";
cat2str[DEFENSE] = "DEFENSE";
cat2str[JAMMER] = "JAMMER";
cat2str[NUKE] = "NUKE";
cat2str[ANTINUKE] = "ANTINUKE";
cat2str[PARALYZER] = "PARALYZER";
cat2str[TORPEDO] = "TORPEDO";
cat2str[TRANSPORT] = "TRANSPORT";
cat2str[EBOOSTER] = "EBOOSTER";
cat2str[MBOOSTER] = "MBOOSTER";
cat2str[SHIELD] = "SHIELD";
cat2str[NANOTOWER] = "NANOTOWER";
cat2str[REPAIRPAD] = "REPAIRPAD";
cat2str[WIND] = "WIND";
cat2str[TIDAL] = "TIDAL";
assert(cat2str.size() == MAX_CATEGORIES);
}
if (str2cat.empty()) {
/* Create the str2cat table and cats vector */
UnitCategory2StrMap::iterator i;
for (i = cat2str.begin(); i != cat2str.end(); ++i) {
cats.push_back(i->first);
str2cat[i->second] = i->first;
}
}
maxUnitPower = 0.0f;
numUnits = ai->cb->GetNumUnitDefs();
/* Build the techtree, note that this is actually a graph in XTA */
buildTechTree();
bool success = false;
unsigned int lastFlags = GET_CAT;
unsigned int flagsOrder[] = { GET_CAT|GET_VER|GET_TEAM, GET_CAT|GET_VER, GET_CAT|GET_TEAM, GET_CAT };
std::string filename;
for (int i = 0; i < sizeof(flagsOrder) / sizeof(unsigned int); i++) {
lastFlags = flagsOrder[i];
filename = ai->cfgparser->getFilename(lastFlags);
if (ai->cfgparser->fileExists(filename)) {
success = ai->cfgparser->parseCategories(filename, units);
if (success)
break;
}
else
LOG_WW("CUnitTable::CUnitTable Categorization file not found: " cb, std::string(CFG_FOLDER) + filename, false);
generateCategorizationFile(filename);
}
filename = ai->cfgparser->getFilename(lastFlags|GET_PATCH);
if (ai->cfgparser->fileExists(filename))
ai->cfgparser->parseCategories(filename, units, true);
else
LOG_WW("CUnitTable::CUnitTable Categorization patch-file not found: " second);
debugCategories(utParent);
debugUnitDefs(utParent);
debugWeapons(utParent);
canBuild = buildBy = "";
for (l = utParent->buildBy.begin(); l != utParent->buildBy.end(); l++) {
std::stringstream out;
out first;
buildBy += l->second->def->name + "(" + out.str() + "), ";
}
buildBy = buildBy.substr(0, buildBy.length() - 2);
for (l = utParent->canBuild.begin(); l != utParent->canBuild.end(); l++) {
std::stringstream out;
out first;
canBuild += l->second->def->name + "(" + out.str() + "), ";
}
canBuild = canBuild.substr(0, canBuild.length() - 2);
}
*/
LOG_II("CUnitTable::CUnitTable Number of unit types: " GetModShortName());
const std::string modVersion(ai->cb->GetModVersion());
std::ofstream file(fileName.c_str(), std::ios::trunc);
file cats;
if (bid > 0)
builders[bid] = false;
activeUnits[uid] = unit;
idle[bid] = false;
idle[uid] = false;
if ((cats&MOBILE).any() && bid >= 0) {
const unitCategory bcats = activeUnits[bid]->type->cats;
unit->techlvl = (bcats&TECH1).any() ? TECH1 : unit->techlvl;
unit->techlvl = (bcats&TECH2).any() ? TECH2 : unit->techlvl;
unit->techlvl = (bcats&TECH3).any() ? TECH3 : unit->techlvl;
unit->techlvl = (bcats&TECH4).any() ? TECH4 : unit->techlvl;
unit->techlvl = (bcats&TECH5).any() ? TECH5 : unit->techlvl;
}
// NOTE: remember that NOTA has mobile defenses
if (((cats&STATIC).any() && (cats&ATTACKER).any()) || (cats&DEFENSE).any())
defenses[unit->key] = unit;
if ((cats&ESTORAGE).any())
energyStorages[unit->key] = unit;
if ((cats&FACTORY).any())
factories[unit->key] = unit;
if ((cats&MMAKER).any())
metalMakers[unit->key] = unit;
if ((cats&STATIC).any()) {
staticUnits[unit->key] = unit;
if (unit->isEconomy())
staticEconomyUnits[unit->key] = unit;
if ((cats&(SEA|SUB)).any())
staticWaterUnits[unit->key] = unit;
}
return unit;
}
void CUnitTable::update() {
CUnit* unit;
std::map::iterator i;
for (i = activeUnits.begin(); i != activeUnits.end(); ++i) {
unit = i->second;
if (unit->isMicroing())
unit->microingFrames += MULTIPLEXER;
else
unit->aliveFrames += MULTIPLEXER;
}
}
void CUnitTable::buildTechTree() {
if (!units.empty())
return; // alreay initialized
std::map buildOptions;
std::map::iterator j;
std::vector unitdefs(numUnits);
ai->cb->GetUnitDefList(&unitdefs[0]);
// NOTE: -1 movetype means a graph for aircraft
moveTypes[-1] = NULL;
for (int i = 0; i < numUnits; i++) {
const UnitDef *ud = unitdefs[i];
if (ud == NULL) continue;
std::map::iterator u = units.find(ud->id);
UnitType *utParent, *utChild;
if (u == units.end())
utParent = insertUnit(ud);
else
utParent = &(u->second);
buildOptions = ud->buildOptions;
for (j = buildOptions.begin(); j != buildOptions.end(); ++j) {
ud = ai->cb->GetUnitDef(j->second.c_str());
u = units.find(ud->id);
if (u == units.end())
utChild = insertUnit(ud);
else
utChild = &(u->second);
utChild->buildBy[utParent->def->id] = utParent;
utParent->canBuild[utChild->def->id] = utChild;
}
}
for (int i = 0; i < numUnits; i++) {
const UnitDef *ud = unitdefs[i];
if (ud == NULL) continue;
units[ud->id].cats = categorizeUnit(&units[ud->id]);
}
}
UnitType* CUnitTable::insertUnit(const UnitDef *ud) {
UnitType ut;
ut.def = ud;
ut.cost = ud->metalCost*METAL2ENERGY + ud->energyCost;
ut.costMetal = ud->metalCost;
ut.energyMake = ud->energyMake - ud->energyUpkeep;
ut.metalMake = ud->metalMake - ud->metalUpkeep;
ut.dps = calcUnitDps(&ut);
units[ud->id] = ut;
// also register pathtype...
MoveData* md = ud->movedata;
if (md)
moveTypes[md->pathType] = md;
if (maxUnitPower < ut.dps)
maxUnitPower = ut.dps;
return &units[ud->id];
}
unitCategory CUnitTable::categorizeUnit(UnitType *ut) {
const UnitDef* ud = ut->def;
unitCategory cats = 0;
//assert(ud->name != "armsonar");
//assert(ud->humanName != "Shark");
if (ud->isCommander)
cats |= COMMANDER;
if (ud->speed > EPS)
cats |= MOBILE;
else
cats |= STATIC;
if (ud->canfly)
cats |= AIR;
if (ud->canhover)
cats |= SEA;
else if (ud->canSubmerge)
cats |= SUB;
else if (ud->movedata) {
if (ud->movedata->subMarine)
cats |= SUB; // submarine
else if (ud->movedata->moveType == MoveData::Ship_Move) {
float heightAboveWater = ai->cb->GetUnitDefHeight(ud->id) - ud->waterline;
if (heightAboveWater < EPS)
cats |= SUB;
else
cats |= SEA;
}
else if (ud->movedata->depth > 100.0f)
cats |= SUB; // amphibious unit
}
else if (ud->floater || ud->waterline > 0.0f || ud->minWaterDepth > 0.0f) {
float heightAboveWater;
if (ud->waterline > 0.0f || ud->floater)
//heightAboveWater = ai->cb->GetUnitDefHeight(ud->id) - ud->waterline;
heightAboveWater = ai->cb->GetUnitDefRadius(ud->id) / 2.0f - ud->waterline;
else
heightAboveWater = -1.0f; // force to SUB
if (heightAboveWater < EPS)
cats |= SUB;
else
cats |= SEA;
}
if ((ud->canhover || ud->minWaterDepth < 0.0f) && !ud->canfly)
cats |= LAND;
if (ud->canAssist)
cats |= ASSISTER;
if (ud->metalStorage / ut->cost > 0.1f)
cats |= MSTORAGE;
if (ud->energyStorage / ut->cost > 0.2f)
cats |= ESTORAGE;
if (ud->makesMetal >= 0.5f && (ud->energyUpkeep > (ud->makesMetal * 40.0f)))
cats |= MMAKER;
if ((ud->energyMake - ud->energyUpkeep) / ut->cost > 0.002
|| ud->tidalGenerator || ud->windGenerator) {
cats |= EMAKER;
if (ud->tidalGenerator)
cats |= TIDAL;
if (ud->windGenerator)
cats |= WIND;
}
if (ud->extractsMetal)
cats |= MEXTRACTOR;
/*
if (ud->radarRadius > 0)
cats |= RADAR;
if (ud->sonarRadius > 0)
cats |= SONAR;
*/
// NOTE: BA's Dragonfly (transport aircraft) jammer radius = 75
if (ud->jammerRadius > 100) {
cats |= JAMMER;
if ((cats&STATIC).any())
cats |= DEFENSE;
}
if (!ud->weapons.empty()) {
cats |= ATTACKER;
if (CUnit::hasTorpedoWeapon(ud->weapons))
cats |= TORPEDO;
if (CUnit::hasParalyzerWeapon(ud->weapons))
cats |= PARALYZER;
else if ((cats&AIR).any() && ud->hoverAttack)
cats |= ASSAULT;
/* 0 = only low, 1 = only high, 2 both */
if ((cats&AIR).none() && ud->highTrajectoryType >= 1)
cats |= ARTILLERY;
if (CUnit::hasAntiAirWeapon(ud->weapons))
cats |= ANTIAIR;
else if (CUnit::hasNukeWeapon(ud->weapons))
cats |= NUKE;
else if (CUnit::hasInterceptorWeapon(ud->weapons))
cats |= ANTINUKE; // TODO: distinguish from EMP
else if (CUnit::hasShield(ud->weapons))
cats |= SHIELD;
if ((cats&STATIC).any() && (cats&NUKE).none())
cats |= DEFENSE;
}
if (ud->canResurrect)
cats |= RESURRECTOR;
// NOTE: we aren't checking for "canMove" because it is usually used
// to set rally point for factory
if (!ud->buildOptions.empty()) {
int kamikazeUnitCount = 0;
std::map::const_iterator j;
cats |= BUILDER;
if ((cats&STATIC).any())
cats |= FACTORY;
// preprocessing stage...
for (j = ud->buildOptions.begin(); j != ud->buildOptions.end(); ++j) {
const UnitDef* canbuild = ai->cb->GetUnitDef(j->second.c_str());
if (canbuild == NULL)
continue;
if (canbuild->canKamikaze)
kamikazeUnitCount++;
if (canbuild->speed < EPS && (cats&FACTORY).any())
// this is a static builder, not a factory
cats &= ~FACTORY;
}
if (kamikazeUnitCount > 4)
cats &= ~(FACTORY|BUILDER);
if ((cats&FACTORY).any()) {
// precise factory type...
for (j = ud->buildOptions.begin(); j != ud->buildOptions.end(); ++j) {
const UnitDef* canbuild = ai->cb->GetUnitDef(j->second.c_str());
if (canbuild == NULL)
continue;
if (canbuild->canfly) {
cats |= AIRCRAFT;
break;
}
if (canbuild->movedata == NULL)
continue;
if (canbuild->movedata->moveFamily == MoveData::KBot
&& ud->minWaterDepth < 0.0f) {
cats |= KBOT;
break;
}
if (canbuild->movedata->moveFamily == MoveData::Tank
&& ud->minWaterDepth < 0.0f) {
cats |= VEHICLE;
break;
}
if (canbuild->movedata->moveFamily == MoveData::Hover) {
cats |= HOVER;
break;
}
if (canbuild->movedata->moveFamily == MoveData::Ship) {
cats |= NAVAL;
break;
}
}
}
/*
// TODO: improve heuristic estimator then uncomment the code below
if ((cats&BUILDER).any()) {
if (ud->metalCost < 2000.0f)
cats |= TECH1;
else
cats |= TECH2;
}
*/
}
if ((cats&ASSISTER).any() && (cats&(BUILDER|FACTORY)).none()) {
// NOTE: default value for "buildDistance" = 128
if (ud->buildDistance < 130.0f)
cats |= REPAIRPAD;
else
cats |= NANOTOWER;
}
if (ud->loadingRadius > 0.0f && ud->transportCapacity > 0)
cats |= TRANSPORT;
/*
// TODO: improve heuristic estimator then uncomment the code below
if ((cats&ATTACKER).any() && (cats&MOBILE).any() && (cats&BUILDER).none() && ud->speed >= 50.0f) {
std::map::iterator i,j;
for (i = ut->buildBy.begin(); i != ut->buildBy.end(); ++i) {
bool isCheapest = true;
UnitType *bb = i->second;
for (j = bb->canBuild.begin(); j != bb->canBuild.end(); ++j) {
if (ut->cost > j->second->cost && !j->second->def->weapons.empty()) {
isCheapest = false;
break;
}
}
if (isCheapest) {
cats |= SCOUTER;
break;
}
}
}
*/
return cats;
}
float CUnitTable::calcUnitDps(UnitType *ut) {
// FIXME: make our own *briljant* dps calc here
return ut->def->power;
}
int CUnitTable::unitCount(unitCategory c) {
int result = 0;
std::map::iterator i;
for (i = activeUnits.begin(); i != activeUnits.end(); ++i) {
if ((c&i->second->type->cats) == c)
result++;
}
return result;
}
int CUnitTable::factoryCount(unitCategory c) {
int result = 0;
std::map::iterator i;
for (i = factories.begin(); i != factories.end(); ++i) {
if ((c&i->second->type->cats) == c)
result++;
}
return result;
}
bool CUnitTable::gotFactory(unitCategory c) {
return factoryCount(c) > 0;
}
void CUnitTable::getBuildables(UnitType* ut, unitCategory include, unitCategory exclude, std::multimap& candidates) {
if (include.none())
return;
unitCategory incEnvCats = (CATS_ENV&include);
std::vector incCats, excCats;
// split categories...
for (unsigned int i = 0; i < cats.size(); i++) {
// NOTE: excluding tags have priority over including tags
if ((exclude&cats[i]).any())
excCats.push_back(cats[i]);
else if ((include&cats[i]).any())
incCats.push_back(cats[i]);
}
std::map::iterator j;
for (j = ut->canBuild.begin(); j != ut->canBuild.end(); ++j) {
bool valid = true;
unitCategory cat = j->second->cats;
for (unsigned int i = 0; i < incCats.size(); i++) {
// NOTE: evironment tags are handled differently: if requested
// AIR, LAND, SEA & SUB in any combination that means having
// at least one match automatically qualifies unit as valid
if ((incCats[i]&CATS_ENV).any()) {
if (incEnvCats.any()) {
// filter by environment tags is active
if ((incEnvCats&cat).none()) {
valid = false;
break;
}
}
}
else if ((incCats[i]&cat).none()) {
valid = false;
break;
}
}
if (valid) {
/* Filter out excludes */
for (unsigned int i = 0; i < excCats.size(); i++) {
if ((excCats[i]&cat).any()) {
valid = false;
break;
}
}
if (valid) {
float cost = j->second->cost;
candidates.insert(std::pair(cost, j->second));
}
}
}
if (candidates.empty())
LOG_WW("CUnitTable::getBuildables no candidates found INCLUDE(" second;
if(unit->def->id == did) {
return unit;
}
}
return NULL;
}
UnitType* CUnitTable::getUnitTypeByCats(unitCategory c) {
std::map::iterator it;
for (it = units.begin(); it != units.end(); ++it) {
if ((it->second.cats&c) == c)
return &(it->second);
}
return NULL;
}
int CUnitTable::setOnOff(std::map& list, bool value) {
int result = 0;
std::map::iterator i;
for (i = list.begin(); i != list.end(); ++i) {
CUnit* unit = i->second;
if (value != unit->isOn()) {
unit->setOnOff(value);
result++;
}
}
return result;
}
std::string CUnitTable::debugCategories(const unitCategory& categories) {
std::string cats("");
UnitCategory2StrMap::iterator i;
for (i = cat2str.begin(); i != cat2str.end(); ++i) {
unitCategory v = categories & i->first;
if (v == i->first)
cats += i->second + " | ";
}
cats = cats.substr(0, cats.length() - 3);
return cats;
}
std::string CUnitTable::debugCategories(UnitType *ut) {
std::string cats("");
UnitCategory2StrMap::iterator i;
for (i = cat2str.begin(); i != cat2str.end(); ++i) {
unitCategory v = ut->cats & i->first;
if (v == i->first)
cats += i->second + " | ";
}
cats = cats.substr(0, cats.length() - 3);
return cats;
}
void CUnitTable::debugUnitDefs(UnitType *ut) {
const UnitDef *ud = ut->def;
sprintf(buf, "metalUpKeep(%0.2f), metalMake(%0.2f), makesMetal(%0.2f), energyUpkeep(%0.2f), energyMake(%0.2f)\n", ud->metalUpkeep, ud->metalMake, ud->makesMetal, ud->energyUpkeep, ud->energyMake);
sprintf(buf, "buildTime(%0.2f), mCost(%0.2f), eCost(%0.2f)\n", ud->buildTime, ud->metalCost, ud->energyCost);
}
void CUnitTable::debugWeapons(UnitType *ut) {
const UnitDef *ud = ut->def;
for (unsigned int i = 0; i < ud->weapons.size(); i++) {
const UnitDef::UnitDefWeapon *w = &(ud->weapons[i]);
sprintf(buf, "Weapon name = %s\n", w->def->type.c_str());
}
}