📄 aai.cpp
字号:
// -------------------------------------------------------------------------
// AAI
//
// A skirmish AI for the TA Spring engine.
// Copyright Alexander Seizinger
//
// Released under GPL license: see LICENSE.html for more information.
// -------------------------------------------------------------------------
#include "AAI.h"
#include <math.h>
#include <stdio.h>
AAI::AAI()
{
// initialize random numbers generator
srand (time(NULL));
brain = 0;
execute = 0;
bt = 0;
ut = 0;
map = 0;
af = 0;
am = 0;
side = 0;
for(int i = 0; i <= MOBILE_CONSTRUCTOR; i++)
{
activeUnits[i] = 0;
futureUnits[i] = 0;
}
activeScouts = futureScouts = 0;
activeBuilders = futureBuilders = 0;
activeFactories = futureFactories = 0;
initialized = false;
}
AAI::~AAI()
{
if(!cfg->initialized)
return;
// save several ai data
fprintf(file, "\nShutting down....\n\n");
fprintf(file, "Unit category active / under construction\n");
for(int i = 0; i <= MOBILE_CONSTRUCTOR; i++)
{
fprintf(file, "%-20s: %i / %i\n", bt->GetCategoryString2((UnitCategory)i), activeUnits[i], futureUnits[i]);
}
fprintf(file, "\nGround Groups: %i\n", group_list[GROUND_ASSAULT].size());
fprintf(file, "\nAir Groups: %i\n", group_list[AIR_ASSAULT].size());
fprintf(file, "\nHover Groups: %i\n", group_list[HOVER_ASSAULT].size());
fprintf(file, "\nSea Groups: %i\n", group_list[SEA_ASSAULT].size());
fprintf(file, "\nSubmarine Groups: %i\n", group_list[SUBMARINE_ASSAULT].size());
fprintf(file, "\nFuture metal/energy request: %i / %i\n", (int)execute->futureRequestedMetal, (int)execute->futureRequestedEnergy);
fprintf(file, "Future metal/energy supply: %i / %i\n", (int)execute->futureAvailableMetal, (int)execute->futureAvailableEnergy);
fprintf(file, "\nFuture/active scouts: %i / %i\n", futureScouts, activeScouts);
// delete buildtasks
for(list<AAIBuildTask*>::iterator task = build_tasks.begin(); task != build_tasks.end(); task++)
{
delete (*task);
}
// save mod learning data
bt->SaveBuildTable();
delete am;
delete brain;
delete execute;
delete ut;
delete af;
delete map;
delete bt;
// delete unit groups
for(int i = 0; i <= MOBILE_CONSTRUCTOR; i++)
{
for(list<AAIGroup*>::iterator group = group_list[i].begin(); group != group_list[i].end(); ++group)
{
delete (*group);
}
}
// delete [] group_list;
fclose(file);
}
void AAI::GotChatMsg(const char *msg, int player) {}
void AAI::EnemyDamaged(int damaged,int attacker,float damage,float3 dir) {}
void AAI::InitAI(IGlobalAICallback* callback, int team)
{
aicb = callback;
cb = callback->GetAICallback();
// open log file
char filename[500];
char buffer[500];
char team_number[3];
#ifdef WIN32
itoa(team, team_number, 10);
#else
snprintf(team_number,10,"%d",team);
#endif
strcpy(buffer, MAIN_PATH);
strcat(buffer, AILOG_PATH);
strcat(buffer, "AAI_log_team_");
strcat(buffer, team_number);
strcat(buffer, ".txt");
ReplaceExtension (buffer, filename, sizeof(filename), ".txt");
cb->GetValue(AIVAL_LOCATE_FILE_W, filename);
file = fopen(filename,"w");
fprintf(file, "AAI %s running mod %s\n \n", AAI_VERSION, cb->GetModName());
// load config file first
cfg->LoadConfig(this);
if(!cfg->initialized)
{
cb->SendTextMsg("Error: Could not load mod and/or general config file, see .../log/AILog.txt for further information",0);
return;
}
// create buildtable
bt = new AAIBuildTable(cb, this);
bt->Init();
// init unit table
ut = new AAIUnitTable(this, bt);
// init map
map = new AAIMap(this);
map->Init();
// init brain
brain = new AAIBrain(this);
// init executer
execute = new AAIExecute(this, brain);
// create unit groups
// group_list = new list<AAIGroup*>[MOBILE_CONSTRUCTOR+1];
group_list.resize(MOBILE_CONSTRUCTOR+1);
// init airforce manager
af = new AAIAirForceManager(this, cb, bt);
// init attack manager
am = new AAIAttackManager(this, cb, bt);
cb->SendTextMsg("AAI loaded", 0);
}
void AAI::UnitDamaged(int damaged, int attacker, float damage, float3 dir)
{
if(damaged < 0)
return;
const UnitDef *def, *att_def;
UnitCategory att_cat, cat;
// filter out commander
if(ut->cmdr != -1)
{
if(damaged == ut->cmdr)
brain->DefendCommander(attacker);
}
def = cb->GetUnitDef(damaged);
if(def)
cat = bt->units_static[def->id].category;
else
cat = UNKNOWN;
// known attacker
if(attacker != -1)
{
att_def = cb->GetUnitDef(attacker);
// filter out friendly fire
if(cb->GetUnitTeam(attacker) == cb->GetMyTeam())
return;
if(att_def)
{
att_cat = bt->units_static[att_def->id].category;
// retreat builders
if(ut->IsBuilder(damaged))
ut->units[damaged].cons->Retreat(att_cat);
if(att_cat >= GROUND_ASSAULT && att_cat <= SUBMARINE_ASSAULT)
{
float3 pos = cb->GetUnitPos(attacker);
AAISector *sector = map->GetSectorOfPos(&pos);
if(sector && !am->SufficientDefencePowerAt(sector, 1.2f))
{
// building has been attacked
if(cat <= METAL_MAKER)
execute->DefendUnitVS(damaged, def, att_cat, &pos, 115);
// builder
else if(ut->IsBuilder(damaged))
execute->DefendUnitVS(damaged, def, att_cat, &pos, 110);
// normal units
else
execute->DefendUnitVS(damaged, def, att_cat, &pos, 105);
}
}
}
}
// unknown attacker
else
{
// set default attacker
float3 pos = cb->GetUnitPos(damaged);
if(pos.y > 0)
att_cat = GROUND_ASSAULT;
else
att_cat = SEA_ASSAULT;
// retreat builders
if(ut->IsBuilder(damaged))
ut->units[damaged].cons->Retreat(att_cat);
// building has been attacked
if(cat <= METAL_MAKER)
execute->DefendUnitVS(damaged, def, att_cat, NULL, 115);
else if(ut->IsBuilder(damaged))
execute->DefendUnitVS(damaged, def, att_cat, NULL, 110);
}
}
void AAI::UnitCreated(int unit)
{
if(!cfg->initialized)
return;
// get unit磗 id and side
const UnitDef *def = cb->GetUnitDef(unit);
int side = bt->GetSideByID(def->id)-1;
UnitCategory category = bt->units_static[def->id].category;
// add to unittable
ut->AddUnit(unit, def->id);
// get commander a startup
if(!initialized && ut->IsDefCommander(def->id))
{
++futureUnits[COMMANDER];
// set side
this->side = side+1;
//debug
fprintf(file, "Playing as %s\n", bt->sideNames[side+1].c_str());
if(this->side < 1 || this->side > bt->numOfSides)
{
cb->SendTextMsg("Error: side not properly set", 0);
fprintf(file, "ERROR: invalid side id %i\n", this->side);
return;
}
// tell the brain about the starting sector
float3 pos = cb->GetUnitPos(unit);
int x = pos.x/map->xSectorSize;
int y = pos.z/map->ySectorSize;
if(x < 0)
x = 0;
if(y < 0 )
y = 0;
if(x >= map->xSectors)
x = map->xSectors-1;
if(y >= map->ySectors)
y = map->ySectors-1;
// set sector as part of the base
if(map->sector[x][y].SetBase(true))
{
brain->AddSector(&map->sector[x][y]);
brain->start_pos = pos;
brain->UpdateNeighbouringSectors();
brain->UpdateBaseCenter();
}
else
{
// sector already occupied by another aai team (coms starting too close to each other)
// choose next free sector
execute->ChooseDifferentStartingSector(x, y);
}
if(map->mapType == WATER_MAP)
brain->ExpandBase(WATER_SECTOR);
else
brain->ExpandBase(LAND_SECTOR);
// now that we know the side, init buildques
execute->InitBuildques();
bt->InitCombatEffCache(this->side);
ut->AddCommander(unit, def->id);
// add the highest rated, buildable factory
execute->AddStartFactory();
// get economy working
execute->CheckRessources();
initialized = true;
return;
}
// construction of building started
else if(category <= METAL_MAKER && category > UNKNOWN)
{
// create new buildtask
AAIBuildTask *task;
try
{
task = new AAIBuildTask(this, unit, def->id, cb->GetUnitPos(unit), cb->GetCurrentFrame());
}
catch(...)
{
fprintf(file, "Exception thrown when allocating memory for buildtask");
}
build_tasks.push_back(task);
float3 pos = cb->GetUnitPos(unit);
// find builder and associate building with that builder
task->builder_id = -1;
for(set<int>::iterator i = ut->constructors.begin(); i != ut->constructors.end(); ++i)
{
if(ut->units[*i].cons->build_pos.x == pos.x && ut->units[*i].cons->build_pos.z == pos.z)
{
ut->units[*i].cons->construction_unit_id = unit;
task->builder_id = ut->units[*i].cons->unit_id;
ut->units[*i].cons->build_task = task;
ut->units[*i].cons->CheckAssistance();
break;
}
}
// add defence buildings to the sector
if(category == STATIONARY_DEF)
{
int x = pos.x/map->xSectorSize;
int y = pos.z/map->ySectorSize;
if(x >= 0 && y >= 0 && x < map->xSectors && y < map->ySectors)
map->sector[x][y].AddDefence(unit, def->id);
}
else if(category == EXTRACTOR)
{
int x = pos.x/map->xSectorSize;
int y = pos.z/map->ySectorSize;
map->sector[x][y].AddExtractor(unit, def->id, &pos);
}
}
}
void AAI::UnitDestroyed(int unit, int attacker)
{
// get unit磗 id
const UnitDef *def = cb->GetUnitDef(unit);
int side = bt->GetSideByID(def->id)-1;
if(!def)
{
fprintf(file, "Error: UnitDestroyed() called with invalid unit id");
return;
}
// get unit's category
UnitCategory category = bt->units_static[def->id].category;
// and position
float3 pos = cb->GetUnitPos(unit);
int x = pos.x/map->xSectorSize;
int y = pos.z/map->ySectorSize;
// check if unit pos is within a valid sector (e.g. aircraft flying outside of the map)
bool validSector = true;
if(x >= map->xSectors || x < 0 || y < 0 || y >= map->ySectors)
validSector = false;
// update threat map
if(attacker && validSector)
{
const UnitDef *att_def = cb->GetUnitDef(attacker);
if(att_def)
map->sector[x][y].UpdateThreatValues((UnitCategory)category, bt->units_static[att_def->id].category);
}
// unfinished unit has been killed
if(cb->UnitBeingBuilt(unit))
{
--futureUnits[category];
--bt->units_dynamic[def->id].requested;
// unfinished building
if(!def->canfly && !def->movedata)
{
// delete buildtask
for(list<AAIBuildTask*>::iterator task = build_tasks.begin(); task != build_tasks.end(); task++)
{
if((*task)->unit_id == unit)
{
(*task)->BuildtaskFailed();
delete *task;
build_tasks.erase(task);
break;
}
}
if(bt->units_static[def->id].category == STATIONARY_DEF && validSector)
map->sector[x][y].RemoveDefence(unit);
}
// unfinished unit
else
{
if(category == SCOUT)
--futureScouts;
if(bt->IsBuilder(def->id))
{
--futureBuilders;
for(list<int>::iterator unit = bt->units_static[def->id].canBuildList.begin(); unit != bt->units_static[def->id].canBuildList.end(); ++unit)
--bt->units_dynamic[*unit].buildersRequested;
}
if(bt->IsFactory(def->id))
{
--futureFactories;
for(list<int>::iterator unit = bt->units_static[def->id].canBuildList.begin(); unit != bt->units_static[def->id].canBuildList.end(); ++unit)
--bt->units_dynamic[*unit].buildersRequested;
}
}
}
else // finished unit/building has been killed
{
activeUnits[category] -= 1;
bt->units_dynamic[def->id].active -= 1;
// update buildtable
if(attacker)
{
const UnitDef *def_att = cb->GetUnitDef(attacker);
if(def_att)
{
int killer = bt->GetIDOfAssaultCategory(bt->units_static[def_att->id].category);
int killed = bt->GetIDOfAssaultCategory((UnitCategory)category);
// check if valid id
if(killer != -1)
{
brain->AttackedBy(killer);
if(killed != -1)
{
bt->UpdateTable(def_att, killer, def, killed);
map->UpdateCategoryUsefulness(def_att, killer, def, killed);
}
}
}
}
// finished building has been killed
if(!def->canfly && !def->movedata)
{
// decrease number of units of that category in the target sector
if(validSector)
{
map->sector[x][y].unitsOfType[category] -= 1;
map->sector[x][y].own_structures -= bt->units_static[def->id].cost;
if(map->sector[x][y].own_structures < 0)
map->sector[x][y].own_structures = 0;
}
// check if building belongs to one of this groups
// side -1 because sides start with 1; array of sides with 0
if(category == STATIONARY_DEF)
{
if(validSector)
map->sector[x][y].RemoveDefence(unit);
// remove defence from map
map->RemoveDefence(&pos, def->id);
}
else if(category == EXTRACTOR)
{
ut->RemoveExtractor(unit);
// mark spots of destroyed mexes as unoccupied
map->sector[x][y].FreeMetalSpot(cb->GetUnitPos(unit), def);
}
else if(category == POWER_PLANT)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -