Contents Back Forward |
3. CSPL, A first analysis |
3.1 First C++ principles Here i'm going to write a couple of info on C language, notice that i've copied them from web tutorials and that they're far from complete, you MUST read tutorials to learn a bit of C, i placed here info on some arguments just to underline that they're really important in CSPL programming. These arguments are:
3.1.1 Global Variables Local variables are declared within the body of a function, and can only be used within that function. This is usually no problem, since when another function is called, all required data is passed to it as arguments. But what happens if we want to realize a variable which is available to all program functions? We need to declare globally our variable: A global variable declaration looks normal, but is located outside any of the program's functions. I suggest you to place your global variables definitions in your header file. The variable is not declared again in the body of the functions which access it. 3.1.2 itoa C function used to convert integer to string. You'll find this function very useful to show game data in a messagebox, so let's examine it deeply (read infos about header files and functions in tutorials) This function is defined in header file stdlib.h its signature is char * itoa ( int value, char * buffer, int radix );The stdlib reference says that itoa (Integer TO Ascii in case you're trying to guess the name meaning...): Convert integer to string.its parameters are: valueValue to be represented as a string.bufferBuffer where to store the resulting string.radixNumeral radix in which value has to be represented, between 2 and 36.Return Value.A pointer to the string.Portability. Not defined in ANSI-C. Supported by some compilers (All win32 compilers as far as i know, so don't worry). Example. /* itoa example */ #include < stdio.h > #include < stdlib.h > main () {    int i;    char buffer [33];    printf ("Enter a number: ");    scanf ("%d",&i);    itoa (i,buffer,10);    printf ("decimal: %s\n",buffer);    itoa (i,buffer,16);    printf ("hexadecimal: %s\n",buffer);    itoa (i,buffer,2);    printf ("binary: %s\n",buffer);    return 0; } Output: Enter a number: 1750 decimal: 1750 hexadecimal: 6d6 binary: 11011010110 3.1.3 \n character \n is a special character which is translated into a new line code, let's explain better this concept with an example: Let's take the following line: printf("Hello World\n"); It is pretty self explanitory. It prints Hello World on the screen. (printf is a std function which prints his first parameter on screen) The \n you will notice doesn't show up on the screen. \n is translated into a new line code. What if you want to print "\n" on screen if \n means new line on screen? Just use a double \ to print a \ character. The code to do that would be: printf("In C \\n means new line"); 3.1.4 #include The preprocessor directive #include is an instruction to read in the entire contents of another file at that point. This is generally used to read in header files for library functions. Header files contain details of functions and types used within the library. They must be included before the program can make use of the library functions. Library header file names are enclosed in angle brackets, < >. These tell the preprocessor to look for the header file in the standard location for library definitions. This is /usr/include for most UNIX systems. For example #include < stdio.h > Another use for #include for the programmer is where multi-file programs are being written. Certain information is required at the beginning of each program file. This can be put into a file called globals.h and included in each program file. Local header file names are usually enclosed by double quotes, " ". It is conventional to give header files a name which ends in .h to distinguish them from other types of file. Our globals.h file would be included by the following line. #include "globals.h" 3.1.5 ExitProcess() ExitProcess is a standard windows function which simply ends the process (terminate the program). It takes one parameter which represent the Exit Status of the process, nothing particularly interesting, anyway it'S good to know how to end our programs ;) Below i'll report what Microsoft's API reference reports about ExitProcess function: The ExitProcess function ends a process and all its threads. | 3.2 Analyzing templates As seen in Chapter I a CSPL project is divided in several source files; these files have a well defined structure and we will try to show it in this section. PHASE 1: CREATING TEMPLATESThe first thing we need to do is to create a new project, CSPLCompanion will automatize this process for us creating also all templates we need to work; Open a DOS Prompt and change directory to CSPL\Tools subdirectory (ie: if you installed CSPL in C:\CSPL just write "cd c:\CSPL\tools" and return) at this point launch CSPLCompanion using the new project name as parameter (ie: type "CSPLCompanion Example1" and return)If all goes well, a screen similar to the following should pop-up (instead of ".\WorkSpace" you should have "Example1"): ![]() At this point press the A key; CSPLCompanion will create a directory named Example1 (under projects subdirectory) and will put all needed template files in it. take a look at Example1 directory; you'll find 3 files: * CSPLClient.h * CSPLClient.cpp * CSPL.res (contains graphic infos pre-compilated; it shouldn't be changed by the designer) PHASE 2: ANALYZING CSPLClient.h fileIn green we have comments (ignored by the compiler) while in blue we have commands (code read by the compiler):/************************************************************************************************************* SCENARIO NAME: <Place Scenario name here> VERSION: <Put the version number (if any) here> DATE: <Place release date here> AUTHOR: <Place author name here> E-MAIL: <Place author e-mail here> MISC: <Place miscellaneous information here> *************************************************************************************************************/ /*********************************************************************************************************** Offset to Map section in memory: DON'T CHANGE THESE DATA IF YOU'RE NOT SURE! (Useless if map thread is not active) ***********************************************************************************************************/ DWORD START_ADDRESS=0x1a50000; DWORD DIM_ADDRESS=0x60000; DWORD DimPattern=12; /*********************************************************************************************************** String to scn filename (Useless if map thread is not active) ***********************************************************************************************************/ LPCTSTR SCNFileName=".//example.scn"; /*********************************************************************************************************** Thread bit mask: By default no thread is active ***********************************************************************************************************/ BYTE ACTIVITY_FLAG=0; /*********************************************************************************************************** Miscellaneous information TitleCSPL:Title on the top bar of CSPL program (For Example: "CSPL - Red Front") AuthorString:Just a string of information about the scenario (it will pop up in the ABOUT Box) ***********************************************************************************************************/ LPCTSTR TitleCSPL="CSPL - "; LPCTSTR AuthorString="Place scenario information here"; /*********************************************************************************************************** Here we have data for sizing CSPL Main Window XWindow,YWindow:Coords of top-left corner of CSPL Window Width:Width of CSPL Window Height:Height of CSPL Window ***********************************************************************************************************/ int XWindow=600;int YWindow=10;int Width=400;int Height=50; /*********************************************************************************************************** Define here other include informations ***********************************************************************************************************/ Letīs see another time the CSPLClient.h template, but this time letīs examine each section separately: /************************************************************************************************************* SCENARIO NAME: <Place Scenario name here> VERSION: <Put the version number (if any) here> DATE: <Place release date here> AUTHOR: <Place author name here> E-MAIL: <Place author e-mail here> MISC: <Place miscellaneous information here> *************************************************************************************************************/ This first section is entirely commented out and gives information about scenario name, author, etc..., it helps designer to manage different projects and to publish the source-code. /*********************************************************************************************************** Offset to Map section in memory: DON'T CHANGE THESE DATA IF YOU'RE NOT SURE! (Useless if map thread is not active) ***********************************************************************************************************/ DWORD START_ADDRESS=0x1a50000; DWORD DIM_ADDRESS=0x60000; DWORD DimPattern=12; /*********************************************************************************************************** String to scn filename (Useless if map thread is not active) ***********************************************************************************************************/ LPCTSTR SCNFileName=".//example.scn"; These two sections are very complex to explain; since this is an introductive chapter the only thing you must understand is that: Both sections are useless if map thread is not used by your CSPL program; The second Section (SCNFileName) contains the path to the SCN file used with CSPL program. Even if the map thread is active, standard values in the first section will work smoothly; so, usually, you don't need to change the first section, and the second section should be changed only if Map thread is used. /*********************************************************************************************************** Thread bit mask: By default no thread is active ***********************************************************************************************************/ BYTE ACTIVITY_FLAG=0; This byte (ACTIVITY_FLAG) controls threads; it is set to 0 by default and this means that no thread will be active. possible values are: ACTIVATE_UNIT (Turns on unit thread) ACTIVATE_CITY (Turns on cities thread) ACTIVATE_CIV (Turns on civs thread) ACTIVATE_WONDER (Turns on wonder thread) ACTIVATE_GLOBAL (Turns on global variables thread) ACTIVATE_MAP (Turns on map thread) ACTIVATE_ATTACK (Turns on attack thread) And all possible combinations of these values with "|" operator: so, for example, if i need to use Unit thread AND map thread i'll write "BYTE ACTIVITY_FLAG=ACTIVATE_UNIT | ACTIVATE_CITY;" instead of "BYTE ACTIVITY_FLAG=0;" /*********************************************************************************************************** Miscellaneous information TitleCSPL:Title on the top bar of CSPL program (For Example: "CSPL - Red Front") AuthorString:Just a string of information about the scenario (it will pop up in the ABOUT Box) ***********************************************************************************************************/ LPCTSTR TitleCSPL="CSPL - "; LPCTSTR AuthorString="Place scenario information here"; In this section you'll place information about CSPL program and its author; TitleCSPL is a string used by CSPL as title of main CSPL windows while AuthorString is just a string showed up in the CSPL About Box. In the following images you can see a CSPL program with TitleCSPL of "CSPL - RedFront V3.0" and the AboutBox with AuthorString of "In Civ2 it is a masterpiece.\nImagine it in Tot with CSPL...\nPlease Nemo, read this box" (Remember that "\n" means new line in C/C++) ![]() /*********************************************************************************************************** Here we have data for sizing CSPL Main Window XWindow,YWindow:Coords of top-left corner of CSPL Window Width:Width of CSPL Window Height:Height of CSPL Window ***********************************************************************************************************/ int XWindow=600;int YWindow=10;int Width=400;int Height=50; This section simply tells CSPL how to draw its main window; The comment explains well the meaning of variables; standard values draws a thin control bar (Height << Width) in the top-left corner of desktop (XWindow >> 0, YWindow > 0). PHASE 3: ANALYZING CSPLClient.csp fileIn the following i'll cut some pieces of source of CSPLClient.csp; this is not important to understand how CSPL works and don't need to be changed./************************************************************************************************************* SCENARIO NAME: <Place Scenario name here> VERSION: <Put the version number (if any) here> DATE: <Place release date here> AUTHOR: <Place author name here> E-MAIL: <Place author e-mail here> MISC: <Place miscellaneous information here> *************************************************************************************************************/ #include "cspl.h" #include "csplclient.h" #include <stdlib.h> //Useful to import itoa standard C function : it converts numbers in strings allowing //the programmer to show Civ data in a messagebox /************************************************************************************************************* THREAD CONTROL ROUTINES : PLACE YOUR CODE IN THE FOLLOWING FUNCTIONS *************************************************************************************************************/ void UnitCheck(Unit Prova) { //Place your code for Unit Thread here } void CityCheck(City Prova) { //Place your code for City Thread here } void WonderCheck() { //Place your code for Wonder Thread here } void MapCheck() { //Place your code for Map Thread here } void GlbCheck(Global Global1) { //Place your code for Global Thread here } void CivCheck(Civ Prova) { //Place your code for Civ Thread here } void AttackCheck(Unit* Attacker,Unit* Attacked,BOOL Winner) { //Place your code for Attack Thread here } void KeyboardCheck(char keypressed) { //Place your code for Keyboard control here } /************************************************************************************************************* CSPL CLIENT TEMPLATE : DON'T TOUCH THIS *************************************************************************************************************/ int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPTSTR lpCmdLine,int nCmdShow) {...} LRESULT CALLBACK WndProc(HWND hWnd,UINT imessage, WPARAM wParam,LPARAM lParam) {...} BOOL MessageCSPL(LPCSTR Testo) {...} int RequestCSPL(LPCSTR Testo) {...} void AboutBox() {...} void AboutScn() {...} Letīs see another time the CSPLClient.csp template, but this time letīs examine each section separately: /************************************************************************************************************* SCENARIO NAME: <Place Scenario name here> VERSION: <Put the version number (if any) here> DATE: <Place release date here> AUTHOR: <Place author name here> E-MAIL: <Place author e-mail here> MISC: <Place miscellaneous information here> *************************************************************************************************************/ Again, this first section is the same we found in CSPLClient.h template. #include "cspl.h" #include "csplclient.h" #include <stdlib.h> //Useful to import itoa standard C function : it converts numbers in strings allowing //the programmer to show Civ data in a messagebox This is the include section; by default it includes cspl.h (the library), csplclient.h (the file analyzed in phase 2) and the standard C/C++ library called stdlib.h (it's useful because defines a function called itoa which converts numbers in corresponding character set, we'll see an example ahead.) If needed the user can add it's include files here. /************************************************************************************************************* THREAD CONTROL ROUTINES : PLACE YOUR CODE IN THE FOLLOWING FUNCTIONS *************************************************************************************************************/ Here the designer place all its code; each thread has a corresponding function: If the designer wants to define a particular event which acts on Units it should redefine Unit function and so on. void UnitCheck(Unit Prova) { //Place your code for Unit Thread here } This function is used to control Unit Thread; how it works: UnitThread continuosly scan Unit List in memory; for each unit in unit list Thread executes UnitCheck. void CityCheck(City Prova) { //Place your code for City Thread here } This function is used to control City Thread; how it works: CityThread continuosly scan cities List in memory; for each city in cities list Thread executes CityCheck. void WonderCheck() { //Place your code for Wonder Thread here } This function is used to control Wonders Thread; how it works: WonderThread doesn't look in any list, it just executes WonderCheck continuously (This means also that there's no technical reason to write in WonderCheck wonder-only code). void MapCheck() { //Place your code for Map Thread here } This function is used to control Map Thread; how it works: MapThread doesn't look in any list, it just executes MapCheck continuously (This means also that there's no technical reason to write in WonderCheck wonder-only code). Anyway, technical reason suggest to use MapThread only if it's really necessary. void GlbCheck(Global Global1) { //Place your code for Global Thread here } This function is used to control Global Thread; how it works: Global Thread continuosly updates Global Data and calls GlbCheck on updated Global data void CivCheck(Civ Prova) { //Place your code for Civ Thread here } This function is used to control Civilizations Thread; how it works: CivThread continuosly scan Civilizations List in memory; for each civ in game Thread executes CivCheck. void AttackCheck(Unit* Attacker,Unit* Attacked,BOOL Winner) { //Place your code for Attack Thread here } This function is used to control Attack Thread; how it works: AttackThread continuosly scan Civilization memory for battles; for each battle in game Thread executes AttackCheck. void KeyboardCheck(char keypressed) { //Place your code for Keyboard control here } This function is used to get keypresses of user; how it works: When the user press a key in CSPL main window, the key pressed is stored and KeyboardCheck is executed /************************************************************************************************************* CSPL CLIENT TEMPLATE : DON'T TOUCH THIS *************************************************************************************************************/ As written explicitly the code following this comment shouldn't be changed (at least if you're not sure what you're doing) The functions defined in this section are just main CSPL routine and graphics pre-made functions; we'll see them in the next section. |
3.3 Example 1: Nothing Now you should understand each section of CSPLClient source files; it's time to realize your first CSPL program. Actually you should have three template files in subdirectory Example1 created by CSPLCompanion; these files are fully functional (even if they do nothing 'cause ACTIVITY_FLAG is set to 0, don't remember what ACTIVITY_FLAG is? turn back and read Phase 2) Passing from source-code to executable file is a complex task which needs the execution of two distinct phases: compiling and linking; fortunately, if well configured, CSPLCompanion should be able to help us in this task. Recover DOS Prompt (remember to change directory to CSPL\Tools dir) and launch "CSPLCompanion Example1". If all goes well, a screen similar to the following should pop-up: ![]() At this time press the key B; if CSPLCompanion is well configured then it should start the compiling phase and the linking phase, warning you if some error came out during one of these phases. When CSPLCompanion will end its work in Example1 directory you should find a lot of new files (they were used by compiler and linker as intermediate files but now you can delete them safely), but the most interesting is CSPLClient.exe; it is your first CSPL program. Let's make a test: double click on CSPLClient.exe, what happens? If all goes well, you should see a MessageBox of CSPL in which CSPL warns you that no TOT window has been found in the system, so CSPL will exit; this happens because CSPL needs Tot loaded in memory to work and so it warns you that it can't work. ![]() Try to launch Tot (it's not necessary to start a game, just launch Tot) and retry to launch CSPLClient.exe This time CSPL should start without any problem, you can select the AboutScenario and AboutCSPL from main menu or exit from CSPL, you can't make anything else since all threads are deactivated but as you can see the system is fully functional. ![]() |
3.4 Using GUI pre-made functions As we saw in section 3.2 the last section of CSPLClient.csp template contains some pre-made graphics functions; Let's see them in detail: /************************************************************************************************************* CSPL CLIENT TEMPLATE : DON'T TOUCH THIS *************************************************************************************************************/ BOOL MessageCSPL(LPCSTR Text) {...} This function replace the windows standard MessageBox; it draws a window with a single OK button, the standard CSPL icon and as caption the Text string passed as parameter In the following image you'll see a MessageBox created using MessageCSPL function, the code used to create that Box is reported: ![]() MessageCSPL("This is a MessageCSPL\nLeft: you can see CSPL icon\n Down: you can see the OK button"); int RequestCSPL(LPCSTR Text) {...} This function is very similar to MessageCSPL, the difference here is that there is no OK button but there are two YES/NO buttons, As in the following example: ![]() RequestCSPL("This is a RequestCSPL\nDown: you can see YES/NO Buttons"); RequestCSPL returns a value: it returns YES_BTN if user selects YES, NO_BTN if user select NO; this can be useful to interact with the scenario player. void AboutBox() {...} This function simply draws the AboutCSPL box; it is standard and should not be changed. ![]() AboutBox(); void AboutScn() {...} This function simply draws the AboutSCN box; it is standard and should not be changed (you can change its contents changing AuthorString String in CSPLClient.h file). ![]() AboutScn();[AuthorString="AboutScn() Example"] |
3.5 Example 2: Hello World! In every programming language book i've read one of the first program example is an useless one which simply prints the string "Hello World!" on screen. It's time to see the CSPL equivalent of this simple program. Due to specific structure of CSPL we need to face two problem: 1) We need to find one thread in which write our code; since we want just to print a message which is not related to civ2 units or cities, or wonders any will do (let's use Unit Thread). 2) Since CSPL threads run continuosly we need some instruction to end program when message is shown (Windows API funtion ExitProcess is exactly what we need). PHASE 1:CREATING NEW PROJECTCreate a new project called Example2 with CSPLCompanion (if you don't remember how to do, this is the last time i explain you the procedure: Open a DOS Prompt, change directory to CSPL\Tools dir and type "CSPLCompanion Example2" and type return)PHASE 2:ACTIVATE UNIT THREADSince we've decided to use UnitThread we need to activate it changing ACTIVITY_FLAG:Open CSPLClient.h and set ACTIVITY_FLAG to ACTIVATE_UNIT; this means change row BYTE ACTIVITY_FLAG=0; to BYTE ACTIVITY_FLAG=ACTIVATE_UNIT; PHASE 3:REWRITING UNITCHECK ROUTINEWe need to modify the routine UnitCheck to show "Hello World!" string and to exit after this. Like i said before Windows gives us a function called ExitProcess which terminate the process currently active; ExitProcess gets also one parameter if we want to set exit-status.For example: ExitProcess(-1); terminate the process exiting with exit-code -1. Our HelloWorld procedure should: 1) Write "Hello World!" string. 2) Exit Process. The first task can be obtained with a single call to MessageCSPL() procedure: MessageCSPL("Hello World!"); The second task can be obtained with a single call to ExitProcess() procedure: ExitProcess(0); Now we have the code of our procedure; it's time to put it into UnitCheck routine: Open CSPLClient.csp in Example2 directory and change the UnitCheck routine; it was: void UnitCheck(Unit Prova) { //Place your code for Unit Thread here } And became void UnitCheck(Unit Prova) { MessageCSPL("Hello World!"); ExitProcess(0); } PHASE 4:COMPILING AND LINKINGNow you should compile and link your source code, save what you've done 'till now (CSPLClient.csp & CSPLClient.h)and launch CSPLCompanion with Example2 as parameter (Remember: DOS Prompt, change directory to CSPL\Tools, type "CSPLCompanion Example2" and then type return), select option B (Compile and link a previously written CSPLclient program) and CSPLCompanion should compile and link your source code. PHASE 5:TESTING EXECUTABLELook at Example2 directory: CSPLCompanion should have created CSPLClient.exe, the executable created using previous source code files. Launch Tot (CSPL programs won't start if Tot is not loaded) and launch CSPLClient.exe.You should see NO message box, what is the problem? why a box containing "Hello World!" text doesn't pop up? Well, the problem is that we decided to use Unit Thread to execute our code, BUT, since Tot is on intro screen there is NO unit list to scan in Tot memory, so CSPL doesn't find a unit and doesn't execute UnitCheck. So, stop CSPLClient executable, start a new game with Tot (it's not important the type of game, you just need to start a game to have a units list in memory), and restart CSPLClient executable. ![]() This time the message box should pop-up very quickly, just press OK button and CSPLClient program should terminate. Compliments, you wrote your second CSPL program. |
3.6 Example 3: RequestCSPL Example In the previous section we learned how to use MessageCSPL in a program, let's see how to use RequestCSPL. RequestCSPL asks user to make a choice pressing the YES button or NO button. In this example we want to show a messagebox with the message "Should I terminate CSPL?", if the user press YES button CSPL terminates, while if the user press NO, CSPL continue to work. Again, we have to face several problems due to particular structure of CSPL: 1) Again we don't have to select a particular thread, any will do, so we'll choose again UnitThread. 2) Since CSPL threads run continuosly we need to ensure ourselves that RequestCSPL messagebox will be shown only once. (The way to solve this is pretty simple and general, the use of a flag, a variable to memorize if MessageBox has been shown or not) First of all we need to activate the Unit Thread, setting the ACTIVITY_FLAG to ACTIVATE_UNIT in CSPLClient.h Then we need one boolean variable to keep return value of RequestCSPL function (remember, YES_BTN if user pressed yes else NO_BTN) Then we need another boolean variable as which should be used as flag (Notice, Flag must be global, this means it should be declared outside of all routines, i usually put global variables in .h source file) So, 'till now, we have: BOOL Answer=FALSE; BOOL Flag=FALSE; //Should be declared as global! Now we need to call RequestCSPL: Answer=RequestCSPL("Should I terminate CSPL?"); At this point the user had chosen YES or NO and Answer keeps the user choice, let's use an if statement (see tutorial) to keep track of user decision if (Answer==TRUE) ExitProcess(0); Now, we never used Flag variable, when CSPL program will be executed, it will execute UnitCheck, UnitCheck will call RequestCSPL and the user is prompted with the "Should I terminate CSPL?" question, if the user choose YES, then CSPL calls ExitProcess(0) and terminate BUT, what happens if user choose NO? From what we've coded simply the ExitProcess won't be called, UnitThread executes another time the UnitCheck and UnitCheck calls RequestCSPL another time, so the user is prompted again with the "Should I terminate CSPL?" question. How can we use Flag to correct this behavior? Well, it's simple, we just initialize Flag to FALSE, and at the first RequestCSPL call, Flag should be set to TRUE, in that way, if Flag is TRUE, it means that RequestCSPL has been showed before and doesn't need to be showed again. Let's write in blue old code while in red the code added to use Flag Inside CSPLClient.h source file: BYTE ACTIVITY_FLAG=ACTIVATE_UNIT BOOL Flag=FALSE; Inside UnitCheck routine (CSPLClient.csp): int Answer=NO_BTN; if (Flag==FALSE) { Answer=RequestCSPL("Should I terminate CSPL?"); if (Answer==YES_BTN) ExitProcess(0); else Flag=TRUE; } Why Flag variable should be defined as global? Well, it's easy, all variables declared inside a routine are valid only while the routine is executed and are not preserved between each call the Flag variable must be used between different executions of UnitCheck routine so it cannot be defined inside UnitCheck, it must be defined outside, as a global variable. Let's test this program; again, create a new project called Example3 using CSPLCompanion as you've done in Example2, edit CSPLClient.h and CSPLClient.csp, then launch Tot and start a game, then launch CSPLClient.exe from example3 directory; you should see the program work smoothly as in the following image: ![]() |