Contents
Back
Forward

12. Keyboard Control


12.1 Keyboard Control overview

From a tech point of view Keyboard Control is not obtained using threads, anyway, since it uses a KeyboardCheck(...) function you can
imagine that the structure works exactly as the previous structures.
Keyboard structure, as the name itself says, is used to interact with the CSPL user using the keyboard.
When a user press a key, windows sends a message to the current active window to warn it of the keypress.
Since i´m not able to intercept messages directed towards Tot (i know, it can be done using hooks, i just don't want to mess with them in this early version, maybe in the future), i´ve decided to catch only messages directed towards CSPL program, so, if a user wants to interact with CSPL using keyboard, he must activate CSPL window by clicking on it and only when the CSPL window is active, the user can press the key.
How can the CSPL developer code keyboard control?
Well, i´ve realized a function called KeyboardCheck(char keypressed) which is called each time a key is pressed with CSPL active and
the parameter keypressed is (as you can imagine) the key pressed by the user.
NOTICE: By default 'x' key is considered by CSPL as a quit message, so don't use it in your CSPL programs, it is reserved.



12.2 Keyboard cycle
Just a couple of lines to explain how KeyboardCheck implementation should be written:

void KeyboardCheck(char keypressed)
{/*Add your Keyboard event here*/}

This is how KeyboardCheck appears as soon as a new project is started using CSPLCompanion.
In the previous section we've seen that KeyboardCheck is called when a key is pressed and the keypressed is stored in keypress variable.
For example let's try to write down a KeyboardCheck which answers to pression of 'e' key, Let's say also that the program simply prints on screen a messagebox saying "Hey, you've pressed 'e'!".

void KeyboardCheck(char keypressed)
{
if (keypressed=='e')   //if key pressed by the user is 'e'
  MessageCSPL("Hey, you've pressed 'e'!");  //print the MessageBox
}

NOTICE: character 'e' is different from character 'E' so, if you don't mind the case of the character you have to modify the previous section; there are a lot of ways to obtain this effect, the simpler consists in changing the if condition:
Instead of
if (keypressed=='e') we can type if (keypressed=='e' || keypressed=='E')



12.3 Example 11 : Artillery
Controlling keyboard is very easy and i've explained you all in the previous section, actually this example is a very interesting one, but doesn't add anything new about Keyboard control, neverthless i think this is a very interesting example so i suggest you to read it all.
Here we will try to build a new kind of unit, the artillery unit.
I know, in Tot we have artillery and howitzer units but, believe me, our artillery unit will be definitely more interesting... Imagine to have an artillery unit able to create a shell unit (a missile one) when player press a key. Obviously, to avoid players creating infinite shells in the same turn, we need to implement a rule, let's say that each time an artillery fires its health bar goes down, when it is under 50% (let's say), we cannot fire but we have to wait that its health bar goes over 50%. Ok, this is exactly what we're going to implement. Let's make some hypothesis about this program, as artillery we will use the old artillery unit, as shells we will use cruise missiles, Artillery won't be able to fire under 50% and each time artillery fires its health bar goes down of 1/3.

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

PHASE 1: UNDERSTANDING WHAT WE NEED

The first thing a CSPL designer should think is : "which thread i need?"
In this situation, we will try to use only Keyboard, forcing all work in KeyboardCheck function.
Since Keyboard isn't managed by a thread you don't have to set any flag to use just KeyboardCheck

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 "The player has pressed the F(ire) key in CSPL window and an artillery unit is selected"
while BODY is "CSPL should create a new shell unit at artillery coordinates."

PHASE 4: CODING THE EVENT

When the user presses the fire key CSPL calls KeyboardCheck with character 'f' (or 'F') as parameter.
We should check if character is really 'f' or 'F', then we should get the current unit and check its type.
If it is an artillery then a new Shell unit must be created only if health is high enough (let's use a separate function to create the new unit and check health)
Let's write a bit of code:
void KeyboardCheck(char keypressed)
{
   if (keypressed=='f' || keypressed=='F')
   {
     Unit current; //creates a new empty Unit
     UnitID(GetCurrentUnit(),&current); //fill Unit with current unit data
     if (current.Type==ARTILLERY)
     {
         FireShell(Unit* current);
     }
   }
}

Now we need to implement the FireShell function;
We know that our new unit properties will be:
pos = The same of artillery unit;
Veteran = False;
Type = UNIT_CRUISE;
Owner = The same of artillery unit;
HealthLost = 0;
Commodity = No sense (even if it must be equal to FuelUsed due to a bug of Civ2);
Orders = None;
HomeCity = None;
MoveLost = 0;
FuelUsed=0; ID = Will be set by CreateUnit library function so we don't care;

Let's code this:
void FireShell(Unit* current)
{
   if (current->HealthLost < 10)
  {
     Unit Shell;
     Shell.pos.x=current.pos.x;
     Shell.pos.y=current.pos.y;
     Shell.pos.z=current.pos.z;
     Shell.Veteran=FALSE;
     Shell.Type=UNIT_CRUISE;
     Shell.Owner=current->Owner;
     Shell.HealthLost=0;
     Shell.Orders=NONE;
     Shell.HomeCity=NONE;
     Shell.MoveLost=0;
     Shell.FuelUsed=0;
     Shell.Commodity=Shell.FuelUsed; //Necessary because Commodity byte and FuelUsed byte are the same one in Civ2

     CreateUnit(Shell);
     current->HealthLost+=7;
     ReWriteUnit(current,current->ID);
  }

}
Notice that since Artillery HP are max 20 (check Tot cheat menu:Edit Unit -> Set Hit Points) 50% is 10 while 33% is 7 (rounded up) and this explain numbers i wrote in red.

PHASE 5: MERGING THE RESULTING SOURCE CODE

Now it's time to merge all source code we've written:

Editing CSPLClient.h:
In CSPLClient.h we don't need to activate any thread:

BYTE ACTIVITY_FLAG=0;

Then we need to write down the signature of user-defined functions:

void FireShell(Unit* current);

And we've finished with CSPLClient.h .

Editing CSPLClient.csp:
We have two things to do:
The first one is to write down the KeyboardCheck implementation:

void KeyboardCheck(char keypressed)
{
   if (keypressed=='f' || keypressed=='F')
   {
     Unit current; //creates a new empty Unit
     UnitID(GetCurrentUnit(),&current); //fill Unit with current unit data
     if (current.Type==ARTILLERY)
     {
         FireShell(Unit* current);
     }
   }
}
And now we need to write down the FireShell function:

void FireShell(Unit* current)
{
   if (current->HealthLost < 10)
  {
     Unit Shell;
     Shell.pos.x=current.pos.x;
     Shell.pos.y=current.pos.y;
     Shell.pos.z=current.pos.z;
     Shell.Veteran=FALSE;
     Shell.Type=UNIT_CRUISE;
     Shell.Owner=current->Owner;
     Shell.HealthLost=0;
     Shell.Orders=NONE;
     Shell.HomeCity=NONE;
     Shell.MoveLost=0;
     Shell.FuelUsed=0;
     Shell.Commodity=Shell.FuelUsed; //Necessary because Commodity byte and FuelUsed
                                     //byte are the same one in Civ2


     CreateUnit(Shell);
     current->HealthLost+=7;
     ReWriteUnit(current,current->ID);
  }

}

And we've finished also with cspl.csp

PHASE 6: COMPILING AND LINKING THE SOURCE CODE

At this point save CSPLClient.h and CSPLClient.csp files and use CSPLCompanion to compile and link Artillery;
you should obtain a CSPLClient executable in Artillery directory,
To test this example you should create a game with an artillery unit; start CSPLClient.exe, select it and press "f" key, you should see artillery health ba which goes down of 1/3 and a new cruiser missile will be created on the same square of artillery.
Notice: Probably you can't see it but under Artillery your cruise missile (shell) will be ready to by fired, you can select it by pressing 'w' (making Artillery wait), Tot is forced to select Cruise Missile (Ok, it's a problem of CSPL, but remember we're writing in Tot memory directly bypassing all controls, obviously some annoyance must came out... even if i think this problem can be solved but i want to put out this version asap so you have to wait the next...).




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