Contents
Back
Forward

6. City Thread


6.1 City Thread overview

In the last chapter we've seen how Unit Thread works.
Here we'll analyze deeply another thread: the City Thread, as we will see City Thread is very similar to Unit Thread and nearly all city functions have a corresponding unit function.
The city thread scan continuosly the city list in memory and, for each unit it executes CityCheck function;
the CSPL designer projects his city-related events just changing the CityCheck function, leaving untouched the main structure of City Thread
The exact source-code of City Thread is the following:

City Temp;

while(true)//Starts a continuos cycle
{
while(ReadNextCity(&Temp))//Select following city in the list
{CityCheck(Temp);} //Call CityCheck function on currently-selected city
ResetCity();//Reset City Pointer
GlobalCheck();//Update Global data (nr of cities particularly)
Sleep(1);//Wait a bit (just to avoid to freeze Tot)
}



6.2 Cities functions
In CSPL i've defined several functions to manage cities:
  • DeleteCity : Deletes a city from the game
  • WriteCity : Replace last city read from memory
  • ReadNextCity : Read next city in unit list
  • CreateCity : Adds a new city in game
  • CityAt : Gets city at particular position
  • CityName : Gets the first city with a particular name string
  • CityID : Gets the city with a particular ID number
  • ReWriteCity : Replace a particular city
  • ResetCity : Reset city list counter, restart scanning city list

void DeleteCity()
Delete last city read from the city list (from the game).

void WriteCity(City Temp)
Replace last city read from Tot memory with Temp city passed as parameter.

bool ReadNextCity(City* Temp)
This function should not be used by CSPL programmers since it is intended for internal library use only, anyway it can be used to scan city list outside from City thread (to see how this function should be used look at section 6.1)

bool CreateCity(City Temp)
This function insert the Temp city passed as parameter inside the city list (inside the Tot game).
Be careful however, if you want to add a new city to a game you've to change also terrain tile in which you want to create the city setting its 'City present' bit (i suggest you to read Allard documentation on hex-edit which is in Doc subdirectory of CSPL).

bool CityAt(DWORD x,DWORD y,DWORD z,DWORD Offset,City* Temp)
This function searches for cities placed at x,y,z (passed as parameter) and places it (if any) in Temp unit structure; if there is no city at x,y,z the function returns FALSE.
Offset parameter is used when you have different cities at the same place, first you call UnitAt with Offset 0 and youŽll get the first city at x,y,z, then call CityAt with Offset 1 and youŽll get the second city at x,y,z, etc. when no more cities are present at x,y,z this function returns FALSE.
Obviously usually only a city can be built at the same coordinates but, since it has been showed that it is possible to have different cities at the same place using hexediting i prefer to leave the Offset parameter, allowing the designer to develop events also in situation in which different cities are placed at the same x,y,z coordinates.

bool CityName(LPSTR Name,DWORD Offset,City* Temp);
This function returns TRUE if a city with the name Name is found in memory (and places its data in Temp unit structure) elsewhere it returns FALSE.
Offset is used if multiple cities with the same name are present in the game; Offset=0 will find the first city, Offset=1 will found the second and so on.
It is intended to be used with special cities (Ex: Who owns Stalingrad actually?)

bool CityID(DWORD Id,City* Temp)
This function returns TRUE if a city with ID equals to Id (passed as parameter) is found (and its data are placed in Temp unit structure).
CityID is used to find a particular city in the city list; if you are interested in a particular city you should save its ID when the game starts and then call CityID with the saved ID to find that particular city late in the game.

bool ReWriteCity(City Temp,DWORD Id)
The ReWriteCity function is used to write Temp city (passed as parameter) in a particular position in the game list (position identified by Id function parameter);
As its name says, this function should be used in quick read-write cycles as the following:
- Read City (Using CityID, CityName, CityAt or other functions).
- Change something on this city.
- Write back the unit calling ReWriteCity with this city ID as Id parameter.

void ResetCity()
This function is intended for internal use only, anyway it reset internal city pointer:
while each call to ReadNextCity function reads the next city in city list, calling ResetCity will reset city pointer so that the next call
to ReadNextCity will read the first city.



6.3 Example 5 : Colonization-like improvements
In Colonization, a Sid Meier's game very similar to Civ (and based on the same engine), city improvements are used in a slight different way respect to Civ improvements:
improvements built in a city decides which units can or cannot be built in that city, so, for example, an artillery unit cannot be built in a city if an armory is not present in that city.
In Civ2 only techs determine which unit can be built and so an event as the one described above is simply impossible to obtain with Tot but again,this effect is possible to obtain with CSPL and we'll try to code exactly this event.

PHASE 0: CREATING A NEW PROJECT

As we've learned in the previous chapters the first step towards CSPL compilation is the project creation (usually done with CSPLCompanion);
again create a new project called ArmoryImprove.

PHASE 1: UNDERSTANDING WHAT WE NEED

The first thing a CSPL designer should think is : "which thread i need?"
In this situation, since we just want to play with city production, our choise is very easy: we need the City Thread.

PHASE 2: DESIGNING THE EVENT

The next thing we have to do is to design the "skeleton" of our event:
From the first chapter we know that event is made of HEAD (Trigger Statement) and BODY (Action Statement):
In this case HEAD is "A city is producing an artillery unit AND there is no armory in the city"
while BODY is "clear building orders of the city and shows a message (only if civ is human-controlled)"

PHASE 3: UNDERSTANDING IMPROVE DATA STRUCTURE

Actually City data structure is just a copy of city data as it is coded in Tot memory (and described in Allard's documentation about hexedit)
Let's quote Allard:
"
53-57 bytes are city improvements:
1... .... is 7th improvement
.1.. .... is 6th improvement
..1. .... is 5th improvement
...1 .... is 4th improvement
.... 1... is 3rd improvement
.... .1.. is 2nd improvement
.... ..1. is 1st improvement
"
What does this means?
This means that we have 5 bytes (53->57), or better, 40 bits and each bit represents an improvement (0 not built, 1 built).
To check if a particular improvement has been built in a city, the designer should find in which block (byte) the bit representing the improvement is packed, and then he should control the bit to see if it is raised or not.
What, it's not clear? i know, let's say we want to check if barracks are present in a city, we have the City structure coded in a City variable called Temp:
Barracks is the third improvement (as listed in the rules.txt, the first is nothing, then we have palace and then barracks) so barracks bit is the third (0000 0100 in bits); this means that the bit is in the "first pack" (byte 0) of Improve data structure. To check that value we can use logic and make an AND between 0000 0100 AND Improve[0]; if this is true (1) barracks are present else it is false (0) and no barracks are present.
In C++ this became Improve[0]&4 (Remember that 4 is 00000100 in decimal)
I know, it's probably less clear, anyway, remember this:
I've coded all previously written numbers as costants, so instead that 4 (0000 0100) you can write IMPROVE_BARRACKS and this should help,
more, if you want to check if a particular improvement has been built you should detect the byte in which it is coded (and this should be easy, the first eight improvements are coded in byte 0, improvements from 9 to 16 in byte 1 and so on) and then call C++ code:

(Improve[i]&IMPROVE_xxxxx)

Where i is the byte nr and xxxxx is the name of the improvement which should be checked.

PHASE 4: CODING THE EVENT

Let's say we've redefined, for our colonization scenario, barracks improvement as armory, and warriors unit as artillery unit,let's say also that the human player is always the white Now it's time to write a couple of lines of code,
'till now we know that:

if (!(City.Improve[0]&IMPROVE_BARRACKS) && City.ItemProduction==UNIT_WARRIOR)
//If City has NOT barracks (Armory) AND City is building warriors (Artillery)
{
City.ItemProduction=BUILD_PALACE; //Change production to Palace
WriteCity(City); //Writeback changes
EmulateKey('c'); //Emulate user c(hange) keypress
}

Where && is the AND operator in C/C++,
& is the bit-wise AND operator and
! is the NOT operator
That function must be repeated for each city in city list and continuosly; it seems that we need to rewrite the CityCheck function

void CityCheck(Unit Temp)
{
if (!(Temp.Improve[0]&IMPROVE_BARRACKS) && Temp.ItemProduction==UNIT_WARRIOR)
//If City has NOT barracks (Armory) AND City is building warriors (Artillery)
{
Temp.ItemProduction=BUILD_PALACE; //Change production to Palace
WriteCity(Temp); //Writeback changes
EmulateKey('c'); //Emulate user c(hange) keypress
}
}
Probably it's a good thing to warn the human player telling him why he is not able to build Artillery (warrior) unit;
For this example let's say also that the Event will act only if the city is human-owned (this means that AI can cheat but in Civ this is nothing new). Let's use the MessageCSPL function with the message "We cannot build Artillery here, Armory needed!"
In code:

void CityCheck(Unit Temp)
{
if (!(Temp.Improve[0]&IMPROVE_BARRACKS) && Temp.ItemProduction==UNIT_WARRIOR && Temp.Owner==CIV_WHITE)
//If City has NOT barracks (Armory) AND City is building warriors (Artillery)
{
MessageCSPL("We cannot build Artillery here, Armory needed!");
Temp.ItemProduction=0xFF; //Change production to nothing
WriteCity(Temp); //Writeback changes
EmulateKey('c'); //Emulate user c(hange) keypress
}
}

PHASE 5: MERGING THE RESULTING SOURCE CODE

Now it's time to merge all source code we've written:

Editing CSPLClient.h:
In CSPLClient.h we need to activate the city thread:

BYTE ACTIVITY_FLAG=ACTIVATE_CITY;

And we've finished with CSPLClient.h .

Editing CSPLClient.csp:
The only thing we've to do here is to edit the CityCheck function as descrived in previous phase:

void CityCheck(Unit Temp)
{
if (!(Temp.Improve[0]&IMPROVE_BARRACKS) && Temp.ItemProduction==UNIT_WARRIOR && Temp.Owner==CIV_WHITE)
//If City has NOT barracks (Armory) AND City is building warriors (Artillery)
{
MessageCSPL("We cannot build Artillery here, Armory needed!");
Temp.ItemProduction=BUILD_PALACE; //Change production to Palace
WriteCity(Temp); //Writeback changes
EmulateKey('c'); //Emulate user c(hange) keypress
}
}

PHASE 6: COMPILING AND LINKING THE SOURCE CODE

At this point save CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link ArmoryImprove;
you should obtain a CSPLClient executable in ArmoryImprove directory,
To test it (without having to rename barracks to armory and warriors to artillery), simply start a new game in Tot, launch CSPLClient.exe and build a city, then try to build a warrior in the city; if all goes well a MessageBox should pop-up warning you that an artillery unit cannot be built in the city because there's no armory in it.


Notice that this example is far from a complete and working CSPL program 'cause, for example, AI is able to build without any problem, again, this code changes production to Palace, while a good idea could be to change production to Armory, in that way, each city which try to build an artillery unit without an armory will start to build one.
Another thing, we could use the city name in the message box, to have a "custom" message as
"We cannot build Artillery in Isabella sir, We need an armory!"




Contents / Introduction
Chapter I / Chapter II / Chapter III / Chapter IV / Chapter V / Chapter VI / Chapter VII
Chapter VIII / Chapter IX / Chapter X / Chapter XI / Chapter XII / Chapter XIII
Appendix A / Appendix B / Appendix C