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 take a deeper look at another thread, the Civilization Thread. The civilization thread continuously scans the civilization list in memory and executes the CivCheck function for each unit. The CSPL designer protects his civ-related events, only changing the CivCheck function and 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 a 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 continuous 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 freezing 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 the 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 the civ list outside of 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 suggests, this function should be used in quick read-write cycles such as the following: - Read Civ (Using mainly CivID or other functions). - Change something on this civ. - Write back the civ calling ReWriteCiv with this civ ID as Id parameter. void ResetCiv() This function is intended for internal use only. Anyway it resets the internal civ pointer while each call to ReadNextCiv function reads the next civ in the 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 the AI started to become more 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 by changing Attitude values directly in memory. First of all draw a couple of lines for this example: Let's say we are creating 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 also assume the USA is the cyan civ (nr 5) while England is the 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). We'll begin by creating a new project called SubmissiveCivs.PHASE 1: UNDERSTANDING WHAT WE NEEDThe first thing a CSPL designer should think is : "which thread do I need?" In this situation, since we just want to play with civ attitude, our choice is very easy. We need the Civ Thread.But we will see this is not strictly necessary, as this particular example could also use the Unit Thread or other threads. PHASE 2: DESIGNING THE EVENTThe next thing we have to do is 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 "The attitude from orange to cyan or vice versa is above a threshold." while BODY is "lower attitude between orange and cyan to a maximum predefined." According to the Civ2 manual, attitude values 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 hex editing). Let's quote Allard:"byte 66-72 Attitudes. Byte 42 is attitude to 1st player, 43 is to 2nd player, etc." What does this mean? 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 the attitude towards the first civ (white civ) and so on. The attitude value varies from 0 (LIKE) to 100 (DISLIKE). PHASE 4: CODING THE EVENTAs we said before, the USA is the cyan civ (civ nr 5) while England is the orange civ (civ nr 6). Keep this in mind. Now it's time to write a couple of lines of code. So far 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 to by constant CIV_CYAN, so the byte corresponding to cyan civ is Attitude[CIV_CYAN], and the same applies for orange civ. LIKEVALUE and THRESHOLD are values pre-defined by the 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 the civs we want to check, so it would be silly to scan the whole civ list just to examine 2 civs. Thus we simply call CivID twice to obtain the civs we're interested in. 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 noted above, the code we've written should be placed in CivCheck:
PHASE 5: 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 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 the AI from breaking the alliance. #define THRESHOLD 50 And we're finished with CSPLClient.h . Editing CSPLClient.csp: The only thing we've got to do here is edit the CityCheck function as described in the previous phase:
void CivCheck(Civ Temp)
PHASE 6: COMPILING AND LINKING THE SOURCE CODEAt this point save the CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link SubmissiveCiv. You should obtain a CSPLClient executable in the SubmissiveCiv directory. Testing this example is somewhat difficult. You should have a game saved with at least two civs (orange and cyan) and you'll need to start CSPLClient.exe. From that point on, the orange and cyan civs should play in "great harmony" and testing attitude values in the cheat menu should always show 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 will first pop-up a message box when Attitude goes over THRESHOLD
(you can check this through the cheat menu) and another message box will 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 because, for example, no action is taken by CSPL to preserve the 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 the attitude between leaders will always be WORSHIPFUL. |