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 another thread in greater depth, the Global Thread. The Global thread continuously scans ToT memory searching for Global information about the game, while simultaneously executing the GlbCheck function. The CSPL designer protects his global-related events by just changing the GlbCheck function, leaving untouched the main structure of Global Thread. There are a lot of differences between Global Thread and the other Threads in CSPL. The exact source-code of Global Thread cannot be reported because it is very complex and uses some of the inner (hidden) CSPL features. Anyway from an external view the Global Thread works exactly as other Threads, so don't be too concerned. | |||||||||||||||||||||||||
9.2 Global functions In CSPL I've defined several functions to manage global variables:
This reports the number of turns passed since the game started. It can be used as a CSPL replacement for the macro-language TURN turn=x command. byte GetDifficulty() This function returns the difficulty level chosen on game start. I've defined the following constants for address difficulty level:
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, which ranges from "Villages Only" to "Raging Hordes". Again, I've defined some constants to help designers:
byte SetBarbarianActivity(byte Activity) Again, this function is the opposite of the previous function, it sets the difficulty level in the 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 the game, FALSE otherwise. byte GetCurrentCiv() This function returns the ID value of the civ currently playing it's 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 a binary number) all civs currently in game. This is exactly the byte 46 in section 2 of Allard's document: enumeration of all civs still in play [binary]Nothing else to add. void CreateCiv(int ID) This function allows the designer to add a new civ to the game (WARNING: you should manually give 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 a 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] void SetHumanPlayer(byte Civs) This function is the opposite of the previous function, and 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:
If we want to set the human controlled civ to a particular one (with id ID) we should do the following:
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 globalHere obviously 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 create an event which switches the player civ after 10 turns. This effect can be used to simulate civil wars in which the player now controls a new faction, or you can use this effect in multi-protagonist scenarios (play the first half with one civ, the second half with another), and a lot of other interesting effects (such as scenarios designed with companies instead of civs, where the player is hired first by one company and then by another, etc). In the following example let's have the player start with the white civ, and at turn 10 the player controlled civ will become the green one. PHASE 0: CREATING A NEW PROJECTAs we've learned in the previous chapters the first step towards CSPL compilation is the project creation (usually done with CSPLCompanion).Start by creating a new project called SwitchRuler. PHASE 1: UNDERSTANDING WHAT WE NEEDThe first thing a CSPL designer should think is : "which thread do I need?"Our choice is simple. Since we want to play with human civs, only GlobalThread is required. PHASE 2: DESIGNING THE EVENTThe next thing we have to do is to design the "skeleton" of our event.From the first chapter we know that each 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 this change happens only once. PHASE 3: CODING THE EVENTWe should check continuously for current turn value, and when it equals to 10, we simply call SetHumanPlayer to change the human controlled civ. All should be done inside the GlbCheck function. It's time to write a bit of code:
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 does SetHumanPlayer need 2^CIV_GREEN as parameter? Well, because SetHumanPlayer is very simple, it writes the parameter in the ToT memory byte which controls HumanPlayer (check Allards documentation for more info). Refresh(): This is a function which forces ToT to repaint the screen. It's useful in a lot of situations because when ToT repaints the screen it also checks a lot of global variables and redesigns the screen according to them. In our example, without the Refresh call, ruler switching will still happen but the player won't notice it until he tries to move a unit or something similar. We should also (though it's not necessary) warn the player of what has happened, using the MessageCSPL function to obtain this result:
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 because the turn is still 10 until the player presses the 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 the flag is false). So, let's write the code: In Csplclient.h we should define the global Flag variable:
Now we should modify the code of GlbCheck:
PHASE 4: MERGING THE RESULTING SOURCE CODENow it's time to merge all the source code we've written:Editing CSPLClient.h: In CSPLClient.h we need to activate the Global thread: BYTE ACTIVITY_FLAG=ACTIVATE_GLOBAL; And we're finished with CSPLClient.h . Editing CSPLClient.csp: The only thing we need to do here is edit the GlobalCheck function as described in the previous phase:
PHASE 5: COMPILING AND LINKING THE SOURCE CODEAt this point save the CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link SwitchRuler. This will create a CSPLClient executable in the SwitchRuler directory.To test this example you should start a game with human-controlled white civ (although since we never wrote that human start civ should be white, we can also start with blue, orange, etc. But the MessageCSPL text will seem strange). Start CSPLClient.exe and play your game. Iif 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 the ruler of the green civ. Notice that this example is far from a finished CSPL event. You should also change the civ information so the green ruler's name reflects your "election", choose a new name for the white ruler, and maybe even change the attitude between your new country and the old one. |