Contents Back Forward |
7. Civilizations Thread | |
7.1 Civilizations Thread overview In the last chapter we've seen how City Thread works. Here we'll analyze deeply another thread: the Civilization Thread. The civilization thread scan continuosly civilization list in memory and, for each unit it executes CivCheck function; the CSPL designer projects his civ-related events just changing the CivCheck function, leaving untouched the main structure of Civ Thread. There are a lot of differences between Civ Thread and City/Unit Threads, for example, Civs are in fixed number (7 + barbarians) while units/cities can vary from 0 to a maximum. The exact source-code of Civ Thread is the following: Civ Temp; while(true)//Starts a continuos cycle { while(ReadNextCiv(&Temp))//Select following civ from the list {CivCheck(Temp);}//Call CivCheck function on currently-selected city ResetCiv();//Reset Civ Pointer GlobalCheck();//Update Global data (nr of civs in game particularly) Sleep(1);//Wait a bit (just to avoid to freeze Tot) } | 7.2 Civilizations functions In CSPL i've defined several functions to manage civs:
void DeleteCiv(int ID) Delete Civ nr ID from game. void WriteCiv(Civ Temp) Replace last civ read from Tot memory with Temp civ passed as parameter. bool ReadNextCiv(Civ* civil) 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 civ list outside from Civ thread (to see how this function should be used look at section 7.1) bool TestCiv(int ID) This function tests if Civ nr ID is currently active. bool CivID(int ID,Civ* civil) This function returns TRUE if a civ with ID equals to Id (passed as parameter) is found (and its data are placed in Civil unit structure). CivID is used to find a particular civ in the civ list. bool ReWriteCiv(Civ Temp,int ID) The ReWriteCiv function is used to write Temp civ (passed as parameter) in a particular position in the civ 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 Civ (Using mainly CivID or other functions). - Change something on this civ. - Write back the unit calling ReWriteCiv with this civ ID as Id parameter. void ResetCiv() This function is intended for internal use only, anyway it reset internal civ pointer: while each call to ReadNextCiv function reads the next civ in civ list, calling ResetCiv will reset civ pointer so that the next call to ReadNextCity will read the first civ. |
7.3 Example 6 : SubmissiveCivs With Civ2:MGE AI started to became aggressive, making scenarios based on alliances between civs very difficult to build. In this example we will try to make alliances between civs more solid, changing Attitude values directly in memory. First of all draw a couple of lines for this example: Let's say we are realizing a scenario based on WWII and we want to implement two civs English and Americans; obviously they should be allied and should keep their alliance for the whole game. Let's say also USA is cyan civ (nr 5) while England is orange civ (nr 6). 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);again create a new project called SubmissiveCivs. PHASE 1: UNDERSTANDING WHAT WE NEEDThe first thing a CSPL designer should think is : "which thread i need?"In this situation, since we just want to play with civ attitude, our choise is very easy: we need the Civ Thread. But as we will see this is not necessary, for this particular example we could use even Unit Thread or other threads. 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 event is made of HEAD (Trigger Statement) and BODY (Action Statement): In this case HEAD is "The attitude from orange to cyan or viceversa is above a threshold." while BODY is "lower attitude between orange and cyan to a maximum predefined." According to Civ2 manual attitude value vary from 0 (LIKE) to 100 (DISLIKE) PHASE 3: UNDERSTANDING ATTITUDE DATA STRUCTUREActually Civ data structure is just a copy of civ data as it is coded in Tot memory (and described in Allard's documentation about hexedit)Let's quote Allard: " byte 66-72 Attitudes. Byte 42 is attitude to 1st player, 43 is to 2nd player, etc. " What does this means? This means that we have 7 bytes (66->72) to keep attitude values, a byte for each civ; as Allard explains the first byte (byte 0) keeps attitude towards first civ (white civ) and so on This attitude value vary from 0 (LIKE) to 100 (DISLIKE). PHASE 4: CODING THE EVENTHow we said before USA is cyan civ (civ nr 5) while England is orange civ (civ nr 6), keep this in mind.Now it's time to write a couple of lines of code, 'till now we know that: Civ Civil; //Obtain Civ orange using CivID function bool StillAlive=CivID(CIV_ORANGE,&Civil); //StillAlive is FALSE if Orange Civ is not in game (England civ has been destroyed) else it is true if (StillAlive && Civil.Attitude[5]>THRESHOLD) //if Orange are still alive and their attitude towards americans is above THRESHOLD { Civil.Attitude[5]=LIKEVALUE; //Lower orange attitude value towards cyan civ to LIKEVALUE ReWriteCiv(Civil,CIV_ORANGE); } //Now we should repeat the procedure above for Cyan civilization //Obtain Civ orange using CivID function StillAlive=CivID(CIV_CYAN,&Civil); //StillAlive is FALSE if Cyan Civ is not in game (USA civ has been destroyed) else it is true if (StillAlive && Civil.Attitude[6]>THRESHOLD) //if Cyan are still alive and their attitude towards England is above THRESHOLD { Civil.Attitude[6]=LIKEVALUE; //Lower cyan attitude value towards orange civ to LIKEVALUE ReWriteCiv(Civil,CIV_CYAN); } Have you noticed those red numbers? well, since i've defined constants for each civ you can use them instead of numbers; This means that Cyan civ (nr 5) is referred by constant CIV_CYAN, so the byte corresponding to cyan civ is Attitude[CIV_CYAN], the same applies for orange civ. LIKEVALUE and THRESHOLD are values pre-defined by designer, we will define them in CSPLClient.h In this example we will use Civ Thread in a strange way; we know the ID of civs we want to check, so it would be silly to scan the whole civ list just to examine 2 civs; so we simply call twice the CivID to obtain civs we're interested into. We still use Civ Thread but we could use every other thread without any problem because we're not using the Civ Temp parameter passed to CivCheck by Civ Thread. As we said above the code we've written should be placed in CivCheck:
PHASE 5: MERGING THE RESULTING SOURCE CODENow it's time to merge all source code we've written:Editing CSPLClient.h: In CSPLClient.h we need to activate the civ thread: BYTE ACTIVITY_FLAG=ACTIVATE_CIV; Then we need to define THRESHOLD and LIKEVALUE: From a quick test we see that an attitude value of 0 means WORSHIPFUL while an attitude value of 50 is FRIENDLY So let's set THRESHOLD to 50 and LIKEVALUE to 0, this should prevent AI from breaking the alliance. #define THRESHOLD 50 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 CivCheck(Civ Temp)
PHASE 6: COMPILING AND LINKING THE SOURCE CODEAt this point save CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link SubmissiveCiv;you should obtain a CSPLClient executable in SubmissiveCiv directory, Testing this example is somewhat difficult, you should have a game saved with at least two civs (orange and cyan) and you should start CSPLClient.exe, from there onward orange and cyan civs should play in a "great armony" and testing attitude values in the cheat menu should show always values over 50. A different way to see this is to force CSPL to warn us when attitude goes under THRESHOLD: This can be done substituting {
with{
In this way CSPL should pop-up a first message box when Attitude goes over THRESHOLD (and you can check this through the cheat menu) and another message box pop-up when the value is set to LIKEVALUE (and again you can check this through the cheat menu).Notice that this example is far from a complete and working CSPL program 'cause, for example, no action is taken by CSPL to preserve alliance, this means that if, for some strange reason (for example because a civ is human-controlled), a civ breaks the alliance and declare war with the other,a war will be fought, the only strange thing is that attitude between leaders is always WORSHIPFUL. |