Contents
Back
Forward

5. Unit Thread


5.1 Unit Thread overview

In the first chapter we've seen how CSPL structure is divided in threads.
Here we'll analyze one of those threads in greater depth: the Unit Thread.
The unit thread continuously scans the unit list in memory, and executes the UnitCheck function for each unit.
The CSPL designer protects his unit events by just changing the UnitCheck function, leaving untouched the main structure of the Unit Thread.
The exact source-code of the Unit Thread is the following:

Unit Temp;

while(true) //Starts a continuous cycle
{
while(ReadNextUnit(&Temp)) //Select following unit in the list
{UnitCheck(Temp);} //Call UnitCheck function on current unit
ResetUnit(); //Reset Unit Pointer
GlobalCheck(); //Update Global data (nr of units particularly)
Sleep(1); //Wait a bit (just to avoid freezing ToT)
}



5.2 Units functions
In CSPL I've defined several functions to manage units:
  • DeleteUnit : Deletes a unit from the game
  • WriteUnit : Replace last unit read from memory
  • ReadNextUnit : Read next unit in unit list
  • CreateUnit : Adds a new unit in game
  • UnitAt : Gets unit at a particular position
  • UnitType : Gets the first unit of a particular type
  • UnitID : Gets the unit with a particular ID number
  • ReWriteUnit : Replace a particular unit
  • ResetUnit : Reset unit list counter, restart scanning unit list

void DeleteUnit()
Delete last unit read from the unit list (from the game).

void WriteUnit(Unit Temp)
Replace last unit read from Tot memory with Temp unit passed as parameter.

bool ReadNextUnit(Unit* unita)
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 the unit list from outside the Units thread (to see how this function should be used look at section 5.1)

bool CreateUnit(Unit Temp)
This function inserts the Temp unit passed as parameter inside the unit list (inside the ToT game).

bool UnitAt(DWORD x,DWORD y,DWORD z,DWORD Offset,Unit* Temp)
This function searches for units placed at x,y,z (passed as parameter) and places it (if any) in Temp unit structure; if there is no unit at x,y,z the function returns FALSE.
Offset parameter is used when you have different units in the stack, first you call UnitAt with Offset 0 and you'll get the first unit at x,y,z, then call UnitAt with Offset 1 and you'll get the second unit at x,y,z, etc. when no more units are present at x,y,z this function returns FALSE.

bool UnitType(DWORD Type,DWORD Offset,Unit* Temp)
This function returns TRUE if a unit of type Type is found in memory (and places its data in the Temp unit structure), otherwise it returns FALSE.
Offset is used if multiple units of type Type are present in the game; Offset=0 will find the first unit, Offset=1 will find the second and so on.
It is intended to be used with special units (Ex: Where is the "Duke of Wellington" unit right now? Is it still alive? and so on)

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

bool ReWriteUnit(Unit Prova,DWORD Id)
The ReWriteUnit function is used to write Temp unit (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 such as the following:
- Read Unit (Using UnitID, UnitType, UnitAt or other functions).
- Change something on this unit.
- Write back the unit calling ReWriteUnit with this unit ID as Id parameter.

void ResetUnit()
This function is intended for internal use only. Anyway it resets the  internal unit pointer:
While each call to ReadNextUnit function reads the next unit in unit list, calling ResetUnit will reset the unit pointer so that the next call
to ReadNextUnit will read the first unit.



5.3 Example 4 : WorldLink
In this example we will see how to link two ToT maps east-west.
This effect is impossible to obtain without CSPL while in CSPL it became quite easy.

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). This step is so basic that I refuse to call it phase 1, hence the phase 0 designation. Begin by creating a new project called WorldLink.

PHASE 1: UNDERSTANDING WHAT WE NEED

The first thing a CSPL designer should ask is, "which thread do I need?"
In this situation, since we just want to move some units from one position to another position, our choice is very easy: We need the Unit 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 the  event is made of HEAD (Trigger Statement) and BODY (Action Statement).
In this case HEAD is "we have a unit on the map border", while BODY is "move that unit to the other side of the map"
If we imagine merging maps at x=0 column we have the following situation:



As you can see, since ToT uses a very strange set of coordinates (odd,odd or even,even), the x=0 column is made up of
(0,0) (1,1) (0,2) (1,3) (0,4) (1,5) ...
This means that a unit is on the left border if its x coordinate is 0 OR 1
and, for the same reason, a unit is on the right border if its x coordinate is XDim-1 OR XDim-2 (where XDim is width of the map in tiles)

PHASE 3: CODING THE EVENT

Now it's time to write a couple of lines of code. Up to now we know that:

if (Unit.pos.x==0 || Unit.pos.x==1) MoveUnitLeft;
else
if (Unit.pos.x==XDim-1 || Unit.pos.x=XDim-2) MoveUnitRight;

Where || is the OR operator in C/C++
that function must be repeated continuously for each unit in the unit list. It seems that we need to rewrite the UnitCheck function:

void UnitCheck(Unit Temp)
{
if (Temp.pos.x==1 || Temp.pos.x==0) MoveLeft(Temp);
else
if (Temp.pos.x==XDim-1 || Temp.pos.x==XDim-2) MoveRight(Temp);
}

Obviously compiling that will produce a lot of errors because MoveLeft and MoveRight are not defined in CSPL nor are they user-defined. Right now these functions simply don't exist. Since you've now been through the recommended C/C++ tutorials, you know how to define new functions and procedures in a C/C++ program, as is the case here.

PHASE 4: CODING AUXILIARY USER-DEFINED FUNCTIONS

We need two functions MoveLeft(Unit) which takes a unit as parameter and changes its position so it appears on the left border of the other map,
and MoveRight(Unit) which takes a unit as parameter and changes its position so it appears on the right border of the other map.

void MoveLeft(Unit Temp)
{
Temp.pos.z=1-Temp.pos.z //Change map (this can be done this way only because we have 2 maps, MAP_0 and MAP_1!)
if (Temp.pos.x==0) Temp.pos.x=XDim-4; //Change X coord. if x was 0
if (Temp.pos.x==1) Temp.pos.x=XDim-3; //Change X coord. if x was 1
WriteUnit(Temp); //Write back unit in memory
RefreshMap(); //Refresh Screen
}

The same happens for MoveRight:

void MoveRight(Unit Temp)
{
Temp.pos.z=1-Temp.pos.z //Change map (this can be done this way only because we have 2 maps, MAP_0 and MAP_1!)
if (Temp.pos.x==XDim-1) Temp.pos.x=3; //Change X coord. if x was Max (XDim-1)
if (Temp.pos.x==XDim-2) Temp.pos.x=2; //Change X coord. if x was Max-1 ((XDim-1)-1)
WriteUnit(Temp); //Write back unit in memory
RefreshMap(); //Refresh Screen
}

To understand the CSPL function RefreshMap() look at MISCELLANEOUS FUNCTIONS

PHASE 5: MERGING THE RESULTING SOURCE CODE

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

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

BYTE ACTIVITY_FLAG=ACTIVATE_UNIT;

Then we need to register new user defined functions:

void MoveLeft(Unit Temp);
void MoveRight(Unit Temp);

Now we're finished with CSPLClient.h .

Editing CSPLClient.csp:
The first step is to edit the UnitCheck function:

void UnitCheck(Unit Temp)
{
if (Temp.pos.x==1 || Temp.pos.x==0) MoveLeft(Temp);
else
if (Temp.pos.x==XDim-1 || Temp.pos.x==XDim-2) MoveRight(Temp);
}

Then we have to add user defined functions:
void MoveLeft(Unit Temp)
{
Temp.pos.z=1-Temp.pos.z; //Change map
if (Temp.pos.x==0) Temp.pos.x=XDim-4; //Change x coord
if (Temp.pos.x==1) Temp.pos.x=XDim-3; //Change x coord
WriteUnit(Temp); //Write back unit
RefreshMap(); //Refresh Screen
}

void MoveRight(Unit Temp)
{
Temp.pos.z=1-Temp.pos.z; //Change map
if (Temp.pos.x==XDim-1) Temp.pos.x=3; //Change x coord
if (Temp.pos.x==XDim-2) Temp.pos.x=2; //Change x coord
WriteUnit(Temp); //Write back unit
RefreshMap(); //Refresh Screen
}

PHASE 6: COMPILING AND LINKING THE SOURCE CODE

At this point save the CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link WorldLink.
You should obtain a CSPLClient executable in the WorldLink directory,
launch ToT, start a game (with 2 worlds!), identify the x=0 column and put a ship (if it is sea) or a fast ground unit (if it is land) near this column (for example on 3,3 tile), start CSPLClient.exe and try moving the unit at (3,3) toward (3,1). The instant the unit enters tile (3,1) it should be transferred to the right border of the second map.

Notice that this example is far from a complete and working CSPL program, because there is no check to see if the starting tile and arriving tile are of the same kind (sea or land). Thus it's possible to have a ship transfer to a land tile and vice versa, tanks arriving in the middle of the ocean on the second map.
This problem can be solved by carefully designing the two maps with the x=0 and adjacent columns full of sea or full of land, to avoid jumping from sea to land and vice versa. A more difficult solution involves checking the arrival tile type and comparing it with the unit which should be moved, and allowing the move ONLY if it's feasible.




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