Contents
Back
Forward

9. Global Thread


9.1 Global Thread overview

In the last chapter we've seen how Wonders Thread works.
Here we'll analyze deeply another thread: the Global Thread.
The Global thread scan continuosly Tot memory searching for Global informations about game, executing at the same time GlbCheck function;
the CSPL designer projects his global-related events just changing the GlbCheck function, leaving untouched the main structure of Global Thread.
There are a lot of differences between Global Thread and other Threads in CSPL:
The exact source-code of Global Thread cannot be reported because it is very complex and uses some of inner CSPL features.
Anyway from an external view the Global Thread works exactly as other Threads, so don't worry too much.


9.2 Global functions
In CSPL i've defined several functions to manage global variables:
  • TurnPassed : returns the number of turns passed from the beginning.
  • GetDifficulty : Get game difficulty level.
  • SetDifficulty : Set game difficulty level.
  • GetBarbarianActivity : Get barbarian activity level currently selected.
  • SetBarbarianActivity : Set barbarian activity level.
  • GetCurrentCiv : Get Civilization which is currently moving.
  • SetCurrentCiv : Set Civilization which is currently moving.
  • GetCurrentUnit : Get Unit which is currently selected.
  • SetCurrentUnit : Set Unit which is currently selected.
  • GetCivInPlay : returns a byte representing Civs currently active.
  • CreateCiv : Adds a particular civilization to game.
  • SetHumanPlayer : Set the human player.
  • GetHumanPlayer : Get the human player currently selected.
  • ReadCurrentPollution : Get current pollution level.
  • WriteCurrentPollution : Set new pollution level.
  • PeaceTurns : Returns the number of peace turns.
WORD TurnPassed()
It reports the number of turns passed since the game started;
it can be used as a CSPL replacement of the macro-language TURN turn=x command.

byte GetDifficulty()
This function returns the difficulty level choosen on game start.
Iīve defined the following costants for address difficulty level:

DIFFICULTY LEVEL CONSTANT DEFINED
Deity Level DIF_DEITY
Emperor Level DIF_EMPEROR
King Level DIF_KING
Prince Level DIF_PRINCE
Warlord Level DIF_WARLORD
Chieftain Level DIF_CHIEFTAIN

byte SetDifficulty(int Level)
This function, as the name itself says, is the opposite of the previous function, it sets the difficulty level in game to Level passed as parameter.

byte GetBarbarianActivity()
This functions returns the level of Barbarian activity, this level range from "Villages Only" to "Raging Hordes"
Again, i've defined some costants to help designers:

BARBARIAN ACTIVITY LEVEL CONSTANT DEFINED
Raging Hordes BA_RAGING
Restless Tribes BA_RESTLESS
Roving Bands BA_ROVING
Villages Only BA_VILLAGES

byte SetBarbarianActivity(byte Activity)
Again, this function is the opposite of the previous function, it sets the difficulty level in game to Level passed as parameter.

BOOL TestCiv(int ID)
this functions takes as parameter a Civ ID (we saw them here) and returns TRUE if that Civ is still in game, FALSE otherwise

byte GetCurrentCiv()
This function returns the ID value of the civ currently playing his turn.

WORD GetCurrentUnit()
This function returns the ID value of the Unit currently selected on screen.
Notice that if no unit is currently selected this function returns 0xFFFF.

byte GetCivsInPlay()
Returns a byte showing (when read as binary number) all civs currently in game;
this is exactly the byte 46 in section 2 of Allard's doc:
enumeration of all civs still in play [binary]
eg. 1001 1101 means: barbarians alive, civ 3 alive, civ 4,5 and 7 alive.
Nothing else to add.

void CreateCiv(int ID)
This function allows designer to add a new civ to the game (WARNING:you should give manually some units or cities to the new civ in order to see it), Just call CreateCiv passing as parameter ID the ID of the civ you want to add.
In case selected civ is already present in the game this function don't do anything.
byte GetHumanPlayer()
Returns a byte showing (when read as binary number) which civs are currently human controlled;
this is exactly the byte 47 in section 2 of Allard's doc:
human player played (can be more than one) [binary]
toggling this byte is great and allows Hotseat mode in FW!!!

void SetHumanPlayer(byte Civs)
This function is the opposite of the previous function, it can set human player while the game is running.
WARNING: It takes as parameter NOT the ID of civs to be human controlled, but the humancivs byte as described in the previous function So, if you just want to add a human civ you should do the following:
  1. Read the current humancivs byte (byte HumanCiv=GetHumanPlayer();)
  2. Set ID bit in HumanCiv byte (HumanCiv=HumanCiv | ID;)
  3. Finally, set the new HumanCiv byte (SetHumanPlayer(HumanCiv);)
( If itīs not clear what is the | operator or why i used it there, then you should read Appendix A )

While if we want to set human controlled civ to a particular one (with id ID) we shoulf do the following:
  1. Calculate ID Civ position in humancivs byte (byte HumanCiv=2^ID;)
  2. Set the new HumanCiv byte (SetHumanPlayer(HumanCiv);)
( If itīs not clear what is the 2 ^ ID equation or why i used it there, then you should read Appendix A )

byte ReadCurrentPollution()
This function returns the current world pollution level.
Let's quote Allard's documentation on this value:
7F is maximum, and will certainly cause global
temperature rising at the end of the turn. A value of 80 till FF is a negative
amount (still shows the icon for global rising), but it will be reset to 00 at the
end of the turn.
Here obvioulsy Allard talks about hex values, this means 7F is 127 decimal while FF is 255 decimal.

void WriteCurrentPollution(byte Level)
Again, this function is the opposite of the previous function, it sets the pollution level in game to Level passed as parameter.

byte PeaceTurns()
Returns the number of (global) peace turns in current game



9.3 Example 8 : SwitchRuler
In this example we will try to realize an event which switch player civ after 10 turns.
This effect can be used to simulate civil wars with the player starting to control a new faction, or you can use this effect in multi-protagonist scenarios (play the first half with one civ, the second half with the other) and a lot of other interesting effects (scenarios designed with companies instead of civs, where player is hired by one company or by another one etc...). In the following let's say that player starts with white civ and, at turn 10, the player controlled civ became the green one.

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 SwitchRuler.

PHASE 1: UNDERSTANDING WHAT WE NEED

The first thing a CSPL designer should think is : "which thread i need?"
Our choise is simple, since we want to play with human civs and so on, only GlobalThread is required.

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 "Current turn is 10."
while BODY is "Change human controlled civ."
WARNING:We should find a way to make sure that change happens only once.

PHASE 3: CODING THE EVENT

We should check continuosly for current turn value, when it is equals to 10, we simply call SetHumanPlayer to change human controlled civ, all should be done inside the GlbCheck function:
Itīs time to write a bit of code:

WORD Turn=TurnPassed();

if (Turn==10)
{
  SetHumanPlayer(pwr(CIV_GREEN));//If turn is equal to 10 then set human player to green
  Refresh();
}


Notice a couple of things:

pwr(CIV_GREEN): this is a function i implemented to calculate in a fast way (without including math lib) 2^CIV_GREEN.
Why SetHumanPlayer needs 2^CIV_GREEN as parameter? well, because SetHumanPlayer is very simple, it writes the parameter in Tot memory byte which controls HumanPlayer (check Allard documentation for more info).

Refresh(): this is a function which forces Tot to repaint the screen, this is useful in a lot of situation because when Tot repaint screen it also check a lot of global variables and redesign the screen according to them; in our example, without Refresh call, ruler switching will happen but the player won't notice it 'till he tries to move a unit or something similar.

We should also (but it's not necessary) to warn player of what has happened, we will use the MessageCSPL function to obtain this result:

WORD Turn=TurnPassed();

if (Turn==10)
{
  MessageCSPL("The White ruler has been driven away by a group of armed rebels,\nGreen Civ has been very happy to receive Player as their leader for skill showed ruling whites,\n Player accepted gratefully and proclamed that his vengeage against whites will be terrible");
  SetHumanPlayer(pwr(CIV_GREEN));//If turn is equal to 10 then set human player to green
Refresh();
}

And that's all, quite simple, isn't it?
But, as i said before, if you try to compile the source, on turn 10 CSPL program goes to loop, it shows you the messagebox and, when you click ok, it shows you the box again (this is due to the fact that turn is still 10 'till the player press EndTurn key).
How to avoid this?
it's easy, we just need to add a global boolean variable, when the game starts this flag will be false and it will be set to true ONLY when the messagebox is displayed (obviously the messagebox will be displayed ONLY if flag is false).
so, let's write the code:
in Csplclient.h we should define the global Flag variable:

BOOL Flag=FALSE;

Now we should modify the code of GlbCheck:

WORD Turn=TurnPassed();

if (Turn==10 && !Flag)
   SetHumanPlayer(pwr(CIV_GREEN));//If turn is equal to 10 then set human player to green
   MessageCSPL("The White ruler has been driven away by a group of armed rebels,\nGreen Civ has been very happy to receive Player as their leader for skill showed ruling whites,\n Player accepted gratefully and proclamed that his vengeage against whites will be terrible");
   Flag=TRUE;
   Refresh();

PHASE 4: 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 Global thread:

BYTE ACTIVITY_FLAG=ACTIVATE_GLOBAL;
Flag=FALSE;


And we've finished with CSPLClient.h .

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

WORD Turn=TurnPassed();

if (Turn==10 && !Flag)
{
SetHumanPlayer(pwr(CIV_GREEN));//If turn is equal to 10 then set human player to green
MessageCSPL("The White ruler has been driven away by a group of armed rebels,\nGreen Civ has been very happy to receive Player as their leader for skill showed ruling whites,\n Player accepted gratefully and proclamed that his vengeage against whites will be terrible");
Flag=TRUE;
Refresh();
}

PHASE 5: COMPILING AND LINKING THE SOURCE CODE

At this point save CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link SwitchRuler;
you should obtain a CSPLClient executable in SwitchRuler directory,
To test this example you should start a game with human-controlled white civ (but, since we never wrote that human start civ should be white we can also start with blue, orange, but the MessageCSPL text will seem strange); start CSPLClient.exe and play your game, if all goes well after ten turns of play you should receive a message informing you of your "Green civ election"
from there onwards you should keep playing as ruler of green civ.
Notice that this example is far from a finished CSPL event, you should think to change also civ information as change green ruler name to reflect your "election" and change white ruler name to a different one, and maybe also change mood between your new country and old one




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