Last update: Civ-Evo 0.6.5
Basic saving implementation:
//data types
const
dRndSeed=0; //random number generator seed
dOldMoney=1; //income from previous turn so can calculate change.
//size is in dwords (multiple of 4 bytes).
function TStdAI.SaveData(dtype,size:integer; var data):integer; //result=ok
begin
result:=eInvalid;
if not(loading) then //probably ok to set loading:=false here
begin
if size>15 then exit;
if dtype>=(65536-CClientEx) shr 4 then exit;
result:=Server(CClientEx+(dtype shl 4)+size,me,0,data);
end;
end;
The save command accepts data like this:
Command:0-2047, (size=0-15 must match data size), data 0-15 words (0-60 bytes).
In the example implementation, the Command part is used to store the type of the save data - this is convenient for loading if you need to save more than one type of data.
To load data you need to handle CClientEx messages in your Client() function,
case Command of
...
else
if command>=CClientEx then //possibly saved data
if AI[Player]<>nil then
AI[PLayer].LoadData(command-CClientEx,data);
You will also receive CClientEx messages every time you save data (the saved message will be echoed back). To know which messages are real and which are echoes you need to know if the game is loading data.
If you use a flag to detect when the game is loading you can set it to true when you get the message cLoadGame and clear it on cTurn (before executing any AI players). cGetReady is not suitable because it is sent before data is loaded.
Here is an example of a loading routine.
procedure TStdAI.LoadData(command:integer; var data);
var
datasize,datatype:integer;
begin
if loading then //will be echo called when savedata() is called.
begin
datasize:=command mod 16; //0...15 dwords
datatype:=command shr 4; //0...(65535-CClientEx)shr 4
//do stuff with saved data
case datatype of
dRndSeed:
RndSeed:=integer(data); //cast to the appropriate type.
dOldMoney:
OldMoney:=integer(data);
end;
end;
end;
The map wraps over at the East and West "edges". To find the minimum spherical distance between two points (and the vector) you test if the vector magnitude is greater than the maximum spherical magnitude and if so (ie if there is a shorter spherical vector) transform the vector to the shorter spherical vector.
This implementation is correct for most cases:
implementation
uses Linear, Protocol, AIClasses; //Linear is from AILib
//returns the distance squared. (Linear spherical vector calculated in dx,dy)
function Distance(FromLoc, ToLoc: CLocation): integer;
var
dx,dy: integer;
begin
dx:=rLoc(Toloc.loc).x-rLoc(FromLoc.loc).x;
dy:=rLoc(Toloc.loc).y-rLoc(FromLoc.loc).y;
//spherical overflow can occur in the direction (1,1) and (-1,-1)
//if the distance>lx div 2 in those directions, spherical overflow occured
if (dx>0) and (dy>0) then
begin
if dx>dy then //dy,dy main
begin
if dy>g.lx div 2 then begin dy:=g.lx-dy; dx:=g.lx-dx; end;
end else //dx,dx main
if dx>g.lx div 2 then begin dy:=g.lx-dy; dx:=g.lx-dx; end;
end;
if (dx<0) and (dy<0) then
begin
if dx<dy then //dy,dy main
begin
if dy<(-g.lx div 2) then begin dy:=g.lx+dy; dx:=g.lx+dx; end;
end else //dx,dx main
if dx<(-g.lx div 2) then begin dy:=g.lx+dy; dx:=g.lx+dx; end;
end;
result:=dx*dx+dy*dy;
end;