Antal1987
Warlord
I've managed to find out how the game prodives an AI.
The following function use AI strategies flags and make units doing some things.
This code calls 19 single-referenced AI functions (one per AI flag except Flag_Unit). Each of these functions either set some unit state or does some action, like moving or attacking.
Unit state values affect on it's behaviour. Depending on the value a Unit can terraform a tile or exploring the land.
There is a function which process unit states and does appropriate action:
Now I've created logging subsystem in my patch framework to trace unit actions (it's not only for units: actually, cities and civilizations are available to be traced too). Download
The patch framework creates "C3CPF.log" and writes log messages there.
Now I have some unknown unit state types. So I'm looking for some volunteers. The task is pretty simple. Log contains info about unit states used to be changed by different unit actions including AI actions. And there are some unrecognized state types. I need more information about which unit types are getting these states and when.
Play just as usual, use different scenarios, involve unit types with different AI strategy flags. My PF will create "C3CPF.log" file with content like this:
The sample shows that one of unrecognized state types (State_29) is set after AI_Terraform action is run. Why? I don't know.
But the more information we collect the more we can inderstand about AI. That could be very useful in fixing submarine bug and other AI misbehavoiurs.
It also can be useful in scenarios tests.
Logging can be turned off. I've created an INI file to store some settings. For now it has only 2 of them:
Important thing is that now you can use different settings for the game
The following function use AI strategies flags and make units doing some things.
Code:
bool __thiscall class_Unit::Process_AI(class_Unit *this)
{
class_Unit *_this; // esi@1
int _Unit_TypeID; // ecx@11
char _AI_Strategy; // al@11
struct_UnitType *_Unit_Type; // ecx@11
unsigned int _AI_Strategy2; // eax@13
int v6; // eax@52
_this = this;
if ( !class_Unit::f51(this) )
{
if ( (1 << Civilizations[_this->Body.CivID].ID) & Global_Civ_Flags2 || !class_Unit::Check_Sacrifice(_this, 0) )
{
if ( _this->Body.CivID )
{
_Unit_TypeID = _this->Body.UnitTypeID;
_AI_Strategy = LOBYTE(BIC_Data.UnitTypes[_Unit_TypeID].AI_Strategy);
_Unit_Type = &BIC_Data.UnitTypes[_Unit_TypeID];
if ( _AI_Strategy & UTAI_Offence )
{
class_Unit::AI_Offence(_this);
}
else
{
_AI_Strategy2 = _Unit_Type->AI_Strategy;
if ( (_Unit_Type->AI_Strategy >> UTAIV_Defence) & 1 )
{
class_Unit::AI_Defence(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Artillery) & 1 )
{
class_Unit::AI_Artillery(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Explore) & 1 )
{
class_Unit::AI_Explore(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Army) & 1 )
{
class_Unit::AI_Army(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Cruise_Missile) & 1 )
{
class_Unit::AI_Cruise_Missile(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Air_Bombard) & 1 )
{
class_Unit::AI_Air_Bombard(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Air_Defence) & 1 )
{
class_Unit::AI_Air_Defence(_this);
}
else
{
if ( BYTE1(_AI_Strategy2) & 1 )// its 8 = UTAIV_Naval_Power
{
class_Unit::AI_Naval_Power(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Air_Transport) & 1 )
{
class_Unit::AI_Air_Transport(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Naval_Transport) & 1 )
{
class_Unit::AI_Naval_Transport(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Naval_Carrier) & 1 )
{
class_Unit::AI_Naval_Carrier(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Terraform) & 1 )
{
class_Unit::AI_Terraform(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Settle) & 1 )
{
class_Unit::AI_Settle(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Leader) & 1 )
{
class_Unit::AI_Leader(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Tactical_Nuke) & 1 )
{
class_Unit::AI_Tactical_Nuke(_this);
}
else
{
if ( BYTE2(_Unit_Type->AI_Strategy) & 1 )// 0x10 = UTAIV_ICBM
{
class_Unit::AI_ICBM(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Naval_Missile_Transport) & 1 )
{
class_Unit::AI_Naval_Missile_Transport(_this);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_Flag_Unit) & 1 )
{
class_Unit::f52_Process_field_1A8_and_IDLS(_this, -1);
class_Unit::Set_Unit_State(_this, 1);
}
else
{
if ( (_AI_Strategy2 >> UTAIV_King) & 1 )
class_Unit::AI_King(_this);
else
class_Unit::Skip_Turn(_this);
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
else
{
class_Unit::f53(_this);
}
}
else
{
if ( !class_Unit::Check_Sacrifice(_this, 0) )
class_Unit::Skip_Turn(_this);
if ( f_Get_City_by_XY(_this->Body.X, _this->Body.Y) )
class_Unit::Sacrifice(_this);
else
class_Unit::f55_Set_Unit_State(_this, UnitState_1D);
}
}
v6 = class_Unit::Get_Default_Hit_Points(_this) - _this->Body.Damage;
return v6 < 0 || v6 <= 9999 && !v6;
}
This code calls 19 single-referenced AI functions (one per AI flag except Flag_Unit). Each of these functions either set some unit state or does some action, like moving or attacking.
Unit state values affect on it's behaviour. Depending on the value a Unit can terraform a tile or exploring the land.
There is a function which process unit states and does appropriate action:
Code:
bool __thiscall class_Unit::m23_Process_Unit_State(class_Unit *this)
{
class_Unit *v1; // esi@1
signed int v2; // ebx@1
int _Unit_State; // eax@1
int v4; // edi@7
int v5; // eax@7
int v6; // edi@10
int v7; // eax@10
int v8; // eax@33
char v9; // dl@45
int v10; // edi@57
int v11; // edi@62
int v12; // eax@69
bool result; // al@70
v1 = this;
v2 = 0;
_Unit_State = this->Body.UnitState;
if ( _Unit_State >= UnitState_Build_Mines && _Unit_State < UnitState_Intercept )
{
class_Unit::Do_Work(this, _Unit_State - 2);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Go_To )
{
if ( f_Is_Net_Game() && (1 << Civilizations[v1->Body.CivID].ID) & Global_Civ_Flags2 )
{
v4 = v1->Body.Moves;
v5 = class_Unit::Get_Complete_Moves(v1);
if ( f_BoundValue(v5 - v4, 0, 9999) )
{
do
{
if ( v1->Body.UnitState != UnitState_Go_To )
break;
if ( v2 >= 256 )
goto LABEL_12;
class_Unit::Do_Go_To_Movement(v1);
v6 = v1->Body.Moves;
++v2;
v7 = class_Unit::Get_Complete_Moves(v1);
}
while ( f_BoundValue(v7 - v6, 0, 9999) );
if ( v2 < 256 )
goto LABEL_69;
LABEL_12:
v1->Body.Moves = class_Unit::Get_Complete_Moves(v1);
}
}
else
{
class_Unit::Do_Go_To_Movement(v1);
}
}
else
{
if ( _Unit_State == UnitState_Road_To_Tile || _Unit_State == UnitState_Railroad_To_Tile )
{
class_Unit::Do_Build_Road_To_Tile(this);
}
else
{
if ( _Unit_State == UnitState_Build_Colony )
{
class_Unit::Do_Auto_Build_Colony(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Auto_Irrigate )
{
class_Unit::Do_Auto_Irrigate(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Build_Trade_Routes )
{
class_Unit::Do_Build_Trade_Routes(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Auto_Clear_Forest )
{
class_Unit::Do_Auto_Clear_Forest(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Auto_Clear_Swamp )
{
class_Unit::Do_Auto_Clear_Swamp(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Auto_Clear_Pollution )
{
class_Unit::Do_Auto_Clear_Pollution(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Auto_Save_City_Tiles )
{
class_Unit::Do_Auto_Save_City_Tiles(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Explore )
{
if ( f_Is_Net_Game() && (1 << Civilizations[v1->Body.CivID].ID) & Global_Civ_Flags2 )
{
while ( 1 )
{
v8 = class_Unit::Get_Complete_Moves(v1) - v1->Body.Moves;
if ( v8 < 0 )
break;
if ( v8 <= 9999 && !v8 )
break;
if ( v1->Body.UnitState != UnitState_Explore )
break;
class_Unit::f27(v1);
}
}
else
{
class_Unit::f27(v1);
}
}
else
{
if ( _Unit_State == UnitState_1B )
{
class_Unit::f54_Unit_State_1B(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_1C )
{
if ( (this->vtable->m25)()
|| (class_Unit::Check_Ability(v1, UTA_Hidden_Nationality)
|| (BIC_Data.UnitTypes[v1->Body.UnitTypeID].AI_Strategy >> UTAIV_Settle) & 1 ? (v9 = 0) : (v9 = 1),
!class_Leader::f24(&Civilizations[v1->Body.CivID], v1->Body.X, v1->Body.Y, 1, v9, 0, 1, 0, 81, 0)) )
class_Unit::Set_Unit_State(v1, UnitState_Fortifying);
else
class_Unit::f83_Unit_State_1C_1D(v1);
}
else
{
if ( _Unit_State == UnitState_1D )
{
class_Unit::f83_Unit_State_1C_1D(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_1E )
{
class_Unit::f84_Unit_State_1E(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Auto_Bombard )
{
class_Unit::f85_Auto_Bombard(this);
goto LABEL_69;
}
if ( _Unit_State == UnitState_Auto_Air_Bombard )
{
v10 = f_Get_Tile_OccupantID(this->Body.field_1B0[5], this->Body.field_1B0[6], -1, 1);
if ( class_Unit::f86_Check_Auto_Air_Bombard(v1, v1->Body.field_1B0[5], v1->Body.field_1B0[6])
&& (v10 == -1 || Civilizations[v1->Body.CivID].At_War[v10]) )
{
class_Unit::Bombard(v1, v1->Body.field_1B0[5], v1->Body.field_1B0[6]);
goto LABEL_69;
}
}
else
{
if ( _Unit_State == UnitState_21 )
{
v11 = f_Get_Tile_OccupantID(this->Body.field_1B0[5], this->Body.field_1B0[6], -1, 1);
if ( class_Unit::f87_Check_State_21(v1, v1->Body.field_1B0[5], v1->Body.field_1B0[6])
&& (v11 == -1 || Civilizations[v1->Body.CivID].At_War[v11]) )
{
class_Unit::f23_Bombard_Tile(v1, v1->Body.field_1B0[5], v1->Body.field_1B0[6]);
goto LABEL_69;
}
}
else
{
if ( _Unit_State != UnitState_22 )
goto LABEL_69;
}
}
class_Unit::Set_Unit_State(v1, 0);
}
}
}
}
LABEL_69:
v12 = class_Unit::Get_Default_Hit_Points(v1) - v1->Body.Damage;
if ( v12 >= 0 )
{
if ( v12 > 9999 )
v12 = 9999;
result = v12 == 0;
}
else
{
result = 1;
}
return result;
}
Now I've created logging subsystem in my patch framework to trace unit actions (it's not only for units: actually, cities and civilizations are available to be traced too). Download
The patch framework creates "C3CPF.log" and writes log messages there.
Now I have some unknown unit state types. So I'm looking for some volunteers. The task is pretty simple. Log contains info about unit states used to be changed by different unit actions including AI actions. And there are some unrecognized state types. I need more information about which unit types are getting these states and when.
Play just as usual, use different scenarios, involve unit types with different AI strategy flags. My PF will create "C3CPF.log" file with content like this:
Code:
2014-06-11 09:22:58 [Unit AI] Unit #221 (Worker) of Civ #3 (Inca) at [23, 47]; Action: AI_Terraform
2014-06-11 09:22:58 [Unit State] Unit #221 (Worker) of Civ #3 (Inca) at [23, 47] changed state from 0 (Normal) to 29 (State_29)
2014-06-11 09:22:58 [Unit State] Unit #221 (Worker) of Civ #3 (Inca) at [23, 47] changed state from 29 (State_29) to 1 (Fortifying)
But the more information we collect the more we can inderstand about AI. That could be very useful in fixing submarine bug and other AI misbehavoiurs.
It also can be useful in scenarios tests.
Logging can be turned off. I've created an INI file to store some settings. For now it has only 2 of them:
Code:
[main]
log=true
log_time=true
Important thing is that now you can use different settings for the game