Contents Back Forward |
11. Attack Thread |
11.1 Attack Thread Overview In this chapter we will face the last thread: the Attack Thread. Attack Thread continuously scans ToT memory searching for a battle. When a battle takes place the attack thread will wait until it ends and then executes a user-defined AttackCheck. Why does it wait until the end of the battle? For a couple of reasons: 1) Since I'm not able to freeze ToT but only to read/write in its memory, it could be risky to let the user access attack structures while ToT is busy reading/writing to them (as it's clearly doing, since an attack is a dynamic process). 2) In this way the designer can gain more information about the battle, such as who won, who lost, how much health the winner lost during the battle, and so on. Again the exact source-code of Attack Thread is very complex so I decided to omit it here. All you really need to know about using AttackThread is this: AttackCheck will be executed after each battle in the game. | |
11.2 Attack Functions There are no specific attack functions in CSPL, because usually you can access all the structures you need using Unit functions. Anyway, AttackCheck is a special function which requires an explanation because it's different from other XXXXCheck functions we've seen before since it takes parameters: void AttackCheck(Unit* Attacker,Unit* Attacked,BOOL Winner) As you can see AttackCheck takes three parameters: Two pointers to Unit structures (Attacker & Attacked) and one boolean (Winner) Obviously Attacker is the unit which started the battle, Attacked is the unit which has been attacked by attacker, and Winner is a flag which is TRUE if Attacker won the battle, FALSE otherwise. Notice that if Winner is TRUE, Attacked is removed by Civ2, while if Winner is FALSE Attacker is removed. So even though you can still access their data structures (CSPL will back up Attacker & Attacked units to avoid having Civ2 overwrite them with newly built units), if you want to make the loser unit the winner (thus changing the result of the Civ2 battle), it's better to first destroy the winner unit and then create a new unit with values copied from the loser Unit structure. NOTE: Even though you can seemingly change the result of a battle, this cannot be done completely in the case of stacks. If a unit is destroyed all units which are on the same stack will be lost forever since you can't catch the information on them. (This is a Civ2 limitation as exemplified by what happens when you smash a barbarian unit stacked with a leader. Who's the first to guess what I mean?) ;o) | |
11.3 Example 10 : EnhancedUnits In Civilization2, winning a battle against an enemy unit could advance the winner unit to veteran status, but nothing particularly interesting. Compare this with Colonization, where Indians who were able to defeat Europeans soldiers could seize their rifles (or horses), thus changing the Indian unit into a more powerful one (natives+guns). There are a lot of situations in Civ2 where similar functionality would be very useful (in sci-fi or fantasy scenarios for example, apart from the historical example given above). We could also imagine a hierarchy of levels: Starting from level0, units will battle against other units gaining sometimes (for each 10 units killed or something similar) a level advance and thus a change to their unit type. Or you can use the exact opposite. If after a battle the winner unit has a health bar in the red, this means it sustained too much damage and it is then transformed into a weaker unit type (until the player takes it to a friendly city to regenerate, etc, but added complexity of this sort is something I leave for designers to investigate). Now we will use attack thread to give ToT a "Colonization" effect: In this example we will use some powerful level0 units (dragoons), more powerful level1 units (Cavalry), and a weak enemy unit (warrior). In a real scenario, level0 units would probably be very weak, forcing the player to waste a lot of level0 units to obtain some level1 units. But we'll start with stronger level0 units since this is an example and we don't want to spend 50 turns building and wasting units. For the test we will start a new game and using the cheat menu we'll add some dragoons and warriors units. PHASE 0: CREATING A NEW PROJECTAs we learned in the previous chapters the first step towards CSPL compilation is the project creation (usually done with CSPLCompanion);Begin by creating a new project called EnhancedUnits. PHASE 1: UNDERSTANDING WHAT WE NEEDThe first thing a CSPL designer should think is : "which thread do I need?" In this example we will only use AttackThread, and this seems proper since our changes are tied to attack routines.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 "A dragoon has won a battle against a warrior" while BODY is "The winner unit should become a Cavalry unit." PHASE 3: CODING THE EVENTThis time it's easy. Since when AttackCheck is called we know that a battle took place we will start searching for the winner.If Winner is TRUE and attacker is a dragoon and attacked is a warrior we should trigger the event. If Winner is FALSE and attacker is a warrior and attacked is a dragoon we should trigger the event. All other outcomes shouldn't trigger the event. Let's write a bit of code (obviously we are in AttackCheck function so we know that Winner is a boolean indicating the battle winner, while Attacker and Attacked are pointers to Unit structures) The next step is to define the ChangeType(Unit* Temp) function we used
above. This function should change the type of Unit structure pointed to by
the Temp pointer and write back the changed info to ToT memory.Let's see how this can be done:
void ChangeType(Unit* Temp)
We should also add a messagebox telling the player of the unit enhancement:
void ChangeType(Unit* Temp)
Because we have defined a new function (remember your rules of C++), we should remember to add the ChangeType function signature to CSPLClient.h. We also have to make sure the code is written in the AttackCheck function:
PHASE 4: 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 attack thread: BYTE ACTIVITY_FLAG=ACTIVATE_ATTACK; Then we need to write down the signature of user-defined ChangeType function: void ChangeType(Unit* Attacked); And we've finished with CSPLClient.h . Editing CSPLClient.csp: We have two things to do: The first one is to write down the AttackCheck implementation:
And now we need to write down the ChangeType function:
void ChangeType(Unit* Temp)
And we're finished with cspl.csp PHASE 5: COMPILING AND LINKING THE SOURCE CODEAt this point save CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link EnhancedUnits. This will create a CSPLClient executable in the EnhancedUnits directory. To test this example you should create a game with a couple of dragoons units and some enemy warriors nearby. Start CSPLClient.exe and attack the Warriors unit with your Dragoons. Each time you win a battle, your Dragoon unit should become a Cavalry one.Notice that there are many possible ways to modify this event, but this one is probably the most simple. You could change the AttackCheck code to trigger the event with any enemy unit, or to trigger only when the unit is attacking, or only when the dragoon unit is owned by a human, or you can even use global variables to trigger the event only when a unit wins 10 battles, and so on. |