═══ 1. Abstract ═══ Abstract GI is a generic Interface between Games and Input Devices. GI was designed to detach the task of getting and normalizing data from input devices from the task of programming a game. The following picture may clarify this: GAMES INPUT DEVICES ┌───────────────┐ ┌──────────┐ │ Wizard of Wor │<─┐ ┌─<─│ Keyboard │ └───────────────┘ │ │ └──────────┘ ┌───────────────┐ │ ╔══════╗ │ ┌──────────┐ │ Boulder Dash │<─┼──<───║ GI ║<────┼─<─│ Joystick │ └───────────────┘ │ ╚══════╝ │ └──────────┘ ┌───────────────┐ │ │ ┌──────────┐ │ Tron │<─┤ ├─<─│ Straight │ └───────────────┘ │ │ └──────────┘ │ │ ... ... Therefore: o An Input Device knows how to handle data from a device (eg. a Joystick or a keyboard). o A Game does not care what kind of Input Device the user prefers. It just queries GI for user input. Or with other words: GI is a black box that hides all information about the user preferences of input devices from the game. ═══ 2. Highlights ═══ Special Highlights of GI version 1.0 o Interface for Games and Input Devices fully written in C o Source code is available o Sample code for both Games and Input Devices available o GI is configured once for all Games! o Possibility for per-Game configuration exists o Analog Input Devices can be used with Games requireing digital user input and vice versa (although this has not been tested yet...) o Up to 10 Players can play simultaneously! ═══ 3. Installation ═══ Installation GI consists of two ZIP-Files: o GIRUN.ZIP (all files needed to run GI) o GISRC.ZIP (all source files for GI) These two ZIP-Files are bound together in GI.ZIP (as you noticed...). If you just want to use GI, you just unzip GIRUN.ZIP. It contains all modules required. The GISRC.ZIP file contains all the source code of GI! You don't need to unzip the TOOLS.ZIP, PMTOOLS.ZIP and STD.ZIP files if you don't recompile GI. Installation step by step 1. Create a directory named "GI". GI is used in more than one game, so this step makes sense even if you don't like it... 2. Copy GIRUN.ZIP to this directory and change (cd) to it. 3. Unzip GIRUN.ZIP. 4. Make sure the following files have been unzipped correctly: o GI.DLL o GICONFIG.EXE 5. Edit your CONFIG.SYS file: a. Append the full drive and path name of the GI-directory to your LIBPATH-entry: LIBPATH=...;C:\GI Otherwise you'll have to put all the dynamic link libraries (*.DLL) of GI to a location already mentioned in your LIBPATH-entry. b. Add the following line to the end of your CONFIG.SYS: SET GICONFIG=C:\GI\GICONFIG.DAT (Assuming you installed GI in a directory "GI" on the C: drive.) c. If you have a PC-Joystick, add the following line to your CONFIG.SYS: DEVICE=C:\GI\GAMEPORT.SYS (Again assuming you installed GI in C:\GI.) 6. Restart your system for the changes in CONFIG.SYS to take effect... That's it. After the restart of your system you can continue by configuring GI. ═══ 4. Configuration ═══ Configuration GI knows two diffrent kinds of configurations: 1. Global/Default configuration 2. Local configuration Basics One of the base ideas behind GI was, that one configuration should be enough for all kind of games. To statisfy this criterion GI has a global configuration file (GICONFIG.DAT) that holds all information for the input devices. This file is located as mentioned in the installation section. But since the restriction to only one configuration may be too hard (imagine a game that uses really weird controls) the possibility for a local configuration was introduced. This local or per-game configuration works just the same way as the global configuration does. The only difference being that the configuration file for the local configuration is located in the directory of the game whereas the global configuration file resides in the GI directory. Configuration of GI Configuration of GI and its input devices is done with the GICONFIG program. This program uses a standard OS/2 Presentation Manager interface. Configuration is performed in two steps: 1. Select an input device from the list or define a new by pressing the New Player button and selecting one from the list presented. (You'll have to select the proper directory first, of course...) Note: Currently available devices are:STRAIGHT.DLL, KBD.DLL, JOY.DLL and TAPEDEV.DLL. See Devices for further information. 2. Start the configure part of the selected device by pressing the Define button or double-clicking in the list of devices. Note: Device 1 will be queryed for input to move player 1, device 2 for player 2, etc.! Special Hint when inserting new devices When you hit the New Player button and no device in the list is highlighted, the new device will be inserted at the end of the list. If there is already a device highlighted, the new one will be inserted before the highlighted device! ═══ 5. Devices ═══ Devices GI cannot work without input devices (see Abstract). The most important devices have already been realized and are part of this version of GI. Since GI is meant to be a generic interface, everybody is encouraged to implement further devices. Detailed info in Writting a new Device. Prepared, but not implemented yet, is the possiblity to implement "intelligent" computer players. Such a computer player device would of course need further information about the rules and strucutres of a game. But for the game (and the game programer) the computer player would be fully transparent. The game would not be able to distinguish a human player from a computer player. This concept hat already been used in realizing the Tape Recorder device. ═══ 5.1. Interface from GI to Devices ═══ Interface from GI to Devices Note: This section is for programers only. Every device is placed in a dynamic link library that gets loaded and called by GI. Therefore all devices have the same interface to GI and use excatly the same mechanism for excanging data. A device must suffice to the following requirements: o Must contain the functions _GetDataSize, _InitializeDLL, _Name, _ConfigDLL, _Threshold and _Direction with the parameters as specified below o Must be callable with std. C-calling conventions The reason for these requirements being that GI comunicates with the device only through these functions. Furtheron, the adresses of these functions are calculated using a call-by-name mechanism (that's why the names are very important). Required functions in a device o _GetDataSize (void): Returns the number of bytes required to hold all data, that the device needs to run (see instance data below). o _InitializeDLL (char __far16 *id): When GI is initialized, the devices are initialized with this function. They receive a pointer to a piece of memory (id) as parameter. The piece of memory has the size returned by the _GetDataSize() function of the device. I recommend to have a flag initialized in the instance data to detect, if the data area has already been initialized with default values or not. o _Name (char __far16 *mm): Copies the name of the device in ascii representation to the memory location (mm) specified by the parameter. This name is shown in the device-list in GICONFIG. o _ConfigDLL (char __far16 *id): Every device should have a configuration part. GI calls this part, when the user selects a device and presses the Define... button. The configuration should have a Presentation Manager interface (like the included devices have). In the configuration part, the user may do all that is needed to use the device for playing afterwards (definitions, calibrations of the device, ...). Note: The selections and definitions that should be saved, must be placed in the instance data. GI will save this area for you! o _Threshold (char __far16 *id): This function is called to convert analog to digital values. Example 1: analog Joystick: device returns value=0..100 GI: if game wants analog values then return value else if value < threshold then return 0 else return 1 Example 2: (digital) Keyboard: device returns value=0 or threshold GI: return value to game As you notice, the threshold value has a different meaning for analog and digital devices. Note: All devices return values in the range 0..100. Digital devices must return the threshold value instead of 1. o _Direction (char __far16 *id, char __far16 *pi): This function is called whenever the game wants the actual values from the device. Parameters are: - id: Pointer to the instance data. - pi: Pointer to the PLAYERINFO-structure given to GI by the game. The device fills the direction[] field of this structure. GI calculates the direction, diagonal and buttons fields afterwards from the values found in the direction[] array. Note: The leading underscore (_) is appended automatically, if you program in C or declare the functions extern "C" in C++. Instance data The same device may be configured for more than one player (eg. two players playing on the same keyboard). GI creates a new instance for every configured use of the device (eg. different keys to move the figures). Therefore all configured data should be placed in the instance data. GI will keep track of saving and reloading the instance data for the devices. Don't create any configuration files and don't use the Profile-functions of Presentation Manager since you cannot be sure that your device will run with PM present! Furtheron you should avoid using global variables even if you define your device to have single nonshared data areas. The reason being that your device may be loaded more than once within the same process and hence sees the same global data in all instances. Writting a new Device ═══ 5.2. Writting a new Device ═══ Writting a new Device It's easy! Note: This section is for programers only. I suggest you copy one of the existing devices (as close as possible to the one you want to implement) and modify it. Some modifications are needed if you don't use Borland C++. But in principle, it's very easy: o Make sure you #include o Define all functions as desrcibed in Interface from GI to device o Implement a minimul functionality and compile your module to a DLL. o Verify the correctness of the names with a library manager (if GI cannot find all functions, it will refuse to load the devie). o Try loading your device in GICONFIG o Implement the full functionality of your device o Send your device to me for distribution in the next release! This step is optional but strongly recomended! Note: If you need to debug your device, you'll need to recompile all parts of GI. To do this you need the sources in TOOLS.ZIP also contained in this distribution. You may use the GIMAKE.CMD command file if you work with Borland C++. ═══ 5.3. Supported Devices ═══ Supported Devices Distributed with GI are some input devices. The source code for these is also contained in the GISRC.ZIP file. Planned are devices for: o Game-Demos (Intros, etc.) o Computer-Player o Mouse (?) Refinement and enrichment of existing devices is also possible... ═══ 5.3.1. Straight Auto-Player ═══ Straight Auto-Player This device was designed to make debugging of games easier. It provides the possibility of an player walking always in the same direction (or standing still). This device is also very usefull to fill the list of installed players... ═══ 5.3.2. Joystick ═══ Joystick There are two kinds of joystick available on the market: o Digital Joysticks and o Analog Joysticks The difference is very simple: Digital joysticks have (mostly) a mechanical switch for every of the 4 directions (North, East, South and West) and the fire-buttons. Analog joysticks have (mostly again) 2 variable resistors which are connected to the stick in a manner, that their resitance changes as the stick is moved in x- or y-direction. The actual value of these resistors gets sampled (10 times per second) and digitized by the game-port. To access the game-port under OS/2 the GAMEPORT.SYS driver must be installed. Note: Support for digital joystick is not yet implemented. Configuring the analog Joystick The values of the resistors in a joystick vary from product to product. To calculate the exact values and scale of the joystick, the resitor values at the extremal points (upper left and lower right corner) must be taken. This is called calibration. The calibration part of the joystick input device configuration can be activated by pressing the Define Max.-button in the joystick configuration. The joystick configuration dialog shows on the left side the analog behaviour of the joystick. If you move the stick, the tiny black point (should) change its position... Note: If the tiny point does not follow the movements of your joystick, JOYSTICK.SYS may not be installed. Check Installation On the right side of the configuration dialog you see the digital representation of the analog values on the left. Depending on the selected threshold the checkboxes check with more or less movement of the stick. We recomend to experiment with the setting for the threshold because this is crucial for the behaviour in games requireing digital input! ═══ 5.3.3. Keyboard ═══ Keyboard The Keyboard Device works both in OS/2 Presentation Manager and OS/2 Full- Screen mode (ever tried to do this?). It intercepts all keyboard activities and checks wheter the actually pressed key (or combination of keys) matches any defined pattern. Configuration 1. Select the direction or button you want to define the key(combination) for. 2. Press the DEFINE button 3. Press the START button in the new dialog window 4. Hit the key(combination). The scan-values and state of the control-keys are displayed. 5. If you hit the correct key(combination) press the STOP button 6. Hit the TEST button and afterwards press your key(combination). If you hear a short beep, your key(combination) was accepted. 7. Hit the OK button, your definition is completed. 8. Go back to point 1 if you want to define more directions or buttons. Note: An asterisk * on the left of a direction or key signals that it has been defined. Warning: Define diferent keys for button 1 and button 2 since some games may distinguish between the buttons! Threshold The Threshold slider comes to action, when the keyboard is used in a game requesting analog input. Since it is impossible to tell how strong a key is typed, the treshold value is used instead of 0 (key not pressed) and 1 (key pressed)... Note: The Keyboard device is also used XFEEL and BUILDER. ═══ 5.3.4. Tape Recorder ═══ Tape Recorder The Tape Recorder consists of two parts: o TAPEDEV.DLL: input device for GI o TAPE.EXE: Program to control the tape The idea of the tape recorder is, that you can monitor and record input from any other device and play it back later. The concept may be known from keyboard-monitors that let you define and play back a series of keystrokes. The Tape Recorder works just this way: You start recording and play the game. All your movements get recorded. Later on you can play this session back. Stop on Overplay option This feature lets you interrupt (better stop) the playing back of a recorded tape. Just let the tape run until you feel to take over. As soon as you move your device you will override the tape and stop its running. Note: This feature can be turned on and off in the Tape Recorder program. Configuring the Tape Recorder 1. Install a Tape Recorder Device (TAPEDEV.DLL) for the player you want to record 2. Configure the Tape Recorder device: The port number must match the device you want to move your figure with. 3. Finish configuring GI Say: You want to monitor player 1 in a game. Input comes from keyboard. Then: You define the tape device a device number 1. The keyboard must also be defined (eg. number 3). Then you configure the tape devie to monitor port number 3. OK? Recording 1. Start the Tape Recorder program (TAPE.EXE) 2. Select a file with the File...-button or enter a filename for the tape. 3. Start your game and make selections and other stuff you don't want to be recorded. 4. Switch back to the Tape Recorder and press the RECORD button. 5. Switch to the game again and play. The Tape Recorder is now recording! 6. As soon as you like, switch back to the Tape Recorder and press the STOP button. Your tape is ready! Playing back 1. Start the Tape Recorder program (TAPE.EXE), but only if it's not running... 2. Select the file to play back. 3. Start your game and move to the point your recording started. 4. Switch back to the Tape Recorder and press the PLAY button. Your tape is running! 5. If the "Stop on Overplay" option is checked, you can stop the show with a single movement from your original device! Note: Play back will stop as soon as the end of the tape is reached. Concept of the Tape Recorder Let's look at it from the point of view of a game. The game wants user input. It calls GI with the number of the player. GI looks at its internal table, which device corresponds to the requested player. Then, GI requests the actual data from the device, normalizes it and returns it to the game. Now the Tape Recorder consists of a Tape Device which can be called from GI as any other device. But the Tape Device does not query any hardware. Instead it calls GI and queries the actual data for the player configured. In Record-mode the data is stored in a file and in Play-back-mode data is loaded from disk instead of querying GI for the original device. ═══ 6. Games ═══ Games All started with Wizard of Wor. At a certain point in the process of developing the game, we had to decide how to get the user input to move the figures. Since we didn't have a joystick or any other input device than mouse and keyboard we "hard coded" the input-part of the game. But of course we were not content with this kind of restriction. And after we released the game to public several people asked for joystick support. Now it would have been easy to simply add this part, but as we were already planing our next game (Boulder Dash), a generic solution to this problem seemed more convenient... Since GI cannot exist without GAMES, we want to encourage everybody to adapt existing games or write new games using GI for the user input! ═══ 6.1. Interface from Game to GI ═══ Interface from Game to GI Note: This section is for programers only. Requirements The game must be programmed in a language that allows C-functions to be called. Therefore the library GI.LIB must be linked to the game that uses GI. There are no problems if the game is written in C or C++, allmost any compiler should be OK... Note: GI.LIB contains just the definitions for the linker to know, that GI.DLL must be loaded to actually execute the GI-functions. Interface functions and data structures There are only two functions and one data structure required for using GI in a game: o PLAYERINFO-structure o GIInitialize() o GIGetDirection (PLAYERINFO *) The prototypings and definitions are in GI.H which must be included to import the functionality of GI. Meanings of data-members in PLAYERINFO-structure typedef struct _playerinfo { // Input Data unsigned player; // Number of actual player BOOL analog; // Game wants analog-values int x; // Actual position x (optional) int y; // Actual position y (optional) // Output Data char dir[6]; // Values of directions: Up, Right, Down, Left, B1, B2 char direction; // Strongest direction or DIRNONE char diagonal; // 2nd strongest direction or DIRNONE char buttons; // 2 bits for buttons // Strategy Data unsigned (*Move) (unsigned x, unsigned y); // Callback to Game! void *stratdata; // Data for Strategy-Instance } PLAYERINFO, *PLAYERINFOP; player: Zero-based number of player for which you want the actual direction and button values. analog: TRUE if game wants analog values in the range of 0..100 for each of the four directions (see below). FALSE if game wants digital input (eg. player moves up, strenght not required). dir[6]: Actual values of the queried device of the 4 directions and the 2 buttons. direction: Strongest/main direction coded as mentioned below. diagonal: Second strongest/secondary direction. If you don't care about diagonal moves, just ignore this field. Otherwise the actual direction is between direction and diagonal (direction=up, diagonal=left --> diagonal left up). buttons: Binary representation of the states of the two buttons. If a button is pressed the corresponding bit is 1. Note: All other fields in PLAYERINFO are not used yet! Coding of the directions #define DIRNONE -1 #define DIRUP 0 #define DIRRIGHT 1 #define DIRDOWN 2 #define DIRLEFT 3 #define DIRBUT1 1 #define DIRBUT2 2 The definitions of the four directions are as well the indices into the direction[] array as the values of the direction and diagonal fields in PLAYERINFO. Consider the following example: /* pi.player and pi.analog must be initialized ! */ if (GIGetDirection (&pi)) { /* A error occured, see errdef.h for further coments... } if (pi.buttons) { /* process buttons now.... */ } if (pi.direction != DIRNONE) /* or use: pi.dir[] .... */ { /* process direction .... */ } Warning: Don't forget to call GIInitialize() somwhere in the startup of your game! Comments o Typically, you allocate a PLAYERINFO-structure for every player your game will support. o The fields for strategy functionality are not yet used, since I don't have a good concept for an example of such a strategy player yet... o The interface was designed to be as narrow as possible to make it easier to use. ═══ 6.2. Games supporting GI ═══ Games supporting GI The three games that we wrote up to now have two things in common: 1. They use GI for getting user input 2. Two players can play at the same time We don't want to make the impression of being "Game-Freaks", but we like playing games and even more than that, we like to write programs. Not necessarly games, well noted. Games are the ideal trade off between writting good software and having some fun afterwards... And by the way: We also wanted to show that OS/2 is a good operating system! ═══ 6.2.1. Wizard of Wor ═══ Wizard of Wor Wizard of Wor is a port from an old C64 game. It was always one of my favourite: simple but fascinating... The OS/2-port was done in C++, all code rewritten. The pictures and dungeons are the same as in the original (took us quite some time to capture all the figures...). Some differences occur in the speed-up during the game and the speed of Worluk and the Wizard. Wizard of Wor is a real CPU-hog. One big problem was, that the maximal timer resolution of OS/2 (32ms) was far too slow. So we decided to do a busy-waiting. The mechanism of slowing the game down in a way that fast maschines are slowed more than slow maschines was invented by Markus and is unique (well sort of...). Note: Our highscore on the original C64 Wizard of Wor is at 194'800 points in two player mode! ═══ 6.2.2. Boulder Dash ═══ Boulder Dash An all time goodie. It exists on almost every game computer. This can be seen as a port of the AMIGA version of Boulder Dash. Most of the ideas for the special objects such as bombs, enemies, swamp, ..., were taken from there. For those how don't know Boulder Dash You (and perhaps your friend) are lost in a system of underground caves. Your task is to collect the diamonds and enter in the house. You can not proceed to the next level until you completed the level... ═══ 6.2.3. Tron ═══ Tron Famous motion picture. A real must for all computer freaks. First movie with computer animated sequences (as far as I know). The game was introduced in the film. Two players, both try to lock each other without crashing into the walls. This game is anything but perfect, many bugs still wait to be fixed... ═══ 7. About 'n Credits ═══ About 'n Credits GI is free! Please use, extend, spread, ... GI as you like. For further enhancments, comments, questions or bugs please send an email or a short note: Roman Fischer Leonhardstr. 12 CH-8001 Zuerich Switzerland rofische@iiic.ethz.ch Credits Special thanks go to Markus Hof, who always helps me out of conceptual troubles and ported Wizard of Wor to GI! Credits to: Andre "Infid" Miodonski, ... Thanks to ICA Systems Inc. for their joystick driver, which is included in the standard version of GI.