rnd-19981123-1
[rocksndiamonds.git] / src / screens.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  screens.c                                               *
12 ***********************************************************/
13
14 #include "screens.h"
15 #include "events.h"
16 #include "sound.h"
17 #include "game.h"
18 #include "tools.h"
19 #include "editor.h"
20 #include "misc.h"
21 #include "files.h"
22 #include "buttons.h"
23 #include "tape.h"
24 #include "joystick.h"
25 #include "cartoons.h"
26 #include "network.h"
27 #include "init.h"
28
29 #ifdef MSDOS
30 extern unsigned char get_ascii(KeySym);
31 #endif
32
33 void DrawHeadline()
34 {
35   int x = SX + (SXSIZE - strlen(GAMETITLE_STRING) * FONT1_XSIZE) / 2;
36
37   DrawText(x, SY + 8, GAMETITLE_STRING, FS_BIG, FC_YELLOW);
38   DrawTextFCentered(46, FC_RED, COPYRIGHT_STRING);
39 }
40
41 void DrawMainMenu()
42 {
43   int i;
44   char *name_text = (!options.network && setup.team_mode ? "Team:" : "Name:");
45
46   FadeSounds();
47   GetPlayerConfig();
48   LoadLevel(level_nr);
49
50   ClearWindow();
51   DrawHeadline();
52   DrawText(SX + 32,    SY + 2*32, name_text, FS_BIG, FC_GREEN);
53   DrawText(SX + 6*32,  SY + 2*32, setup.player_name, FS_BIG, FC_RED);
54   DrawText(SX + 32,    SY + 3*32, "Level:", FS_BIG, FC_GREEN);
55   DrawText(SX + 11*32, SY + 3*32, int2str(level_nr,3), FS_BIG,
56            (leveldir[leveldir_nr].readonly ? FC_RED : FC_YELLOW));
57   DrawText(SX + 32,    SY + 4*32, "Hall Of Fame", FS_BIG, FC_GREEN);
58   DrawText(SX + 32,    SY + 5*32, "Level Creator", FS_BIG, FC_GREEN);
59   DrawText(SY + 32,    SY + 6*32, "Info Screen", FS_BIG, FC_GREEN);
60   DrawText(SX + 32,    SY + 7*32, "Start Game", FS_BIG, FC_GREEN);
61   DrawText(SX + 32,    SY + 8*32, "Setup", FS_BIG, FC_GREEN);
62   DrawText(SX + 32,    SY + 9*32, "Quit", FS_BIG, FC_GREEN);
63
64   DrawMicroLevel(MICROLEV_XPOS,MICROLEV_YPOS);
65
66   for(i=2; i<10; i++)
67     DrawGraphic(0, i, GFX_KUGEL_BLAU);
68   DrawGraphic(10, 3, GFX_PFEIL_L);
69   DrawGraphic(14, 3, GFX_PFEIL_R);
70
71   DrawTextF(15*32 + 6, 3*32 + 9, FC_RED, "%d", leveldir[leveldir_nr].levels);
72
73   DrawText(SX + 56, SY + 326, "A Game by Artsoft Entertainment",
74            FS_SMALL, FC_RED);
75
76   if (leveldir[leveldir_nr].name)
77   {
78     int len = strlen(leveldir[leveldir_nr].name);
79     int lxpos = SX + (SXSIZE - len * FONT4_XSIZE) / 2;
80     int lypos = SY + 352;
81
82     DrawText(lxpos, lypos, leveldir[leveldir_nr].name, FS_SMALL, FC_SPECIAL2);
83   }
84
85   FadeToFront();
86   InitAnimation();
87   HandleMainMenu(0,0, 0,0, MB_MENU_INITIALIZE);
88
89   TapeStop();
90   if (TAPE_IS_EMPTY(tape))
91     LoadTape(level_nr);
92   DrawCompleteVideoDisplay();
93
94   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
95
96   ClearEventQueue();
97   XAutoRepeatOn(display);
98 }
99
100 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
101 {
102   static int choice = 3;
103   static int redraw = TRUE;
104   int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
105
106   if (redraw || button == MB_MENU_INITIALIZE)
107   {
108     DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
109     redraw = FALSE;
110   }
111
112   if (button == MB_MENU_INITIALIZE)
113     return;
114
115   if (dx || dy)
116   {
117     if (dx && choice == 4)
118     {
119       x = (dx < 0 ? 11 : 15);
120       y = 4;
121     }
122     else if (dy)
123     {
124       x = 1;
125       y = choice + dy;
126     }
127     else
128       x = y = 0;
129
130     if (y < 3)
131       y = 3;
132     else if (y > 10)
133       y = 10;
134   }
135
136   if (!mx && !my && !dx && !dy)
137   {
138     x = 1;
139     y = choice;
140   }
141
142   if (y == 4 && ((x == 11 && level_nr > 0) ||
143                  (x == 15 && level_nr < leveldir[leveldir_nr].levels - 1)) &&
144       button)
145   {
146     static long level_delay = 0;
147     int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
148     int new_level_nr, old_level_nr = level_nr;
149     int font_color = (leveldir[leveldir_nr].readonly ? FC_RED : FC_YELLOW);
150
151     new_level_nr = level_nr + (x == 11 ? -step : +step);
152     if (new_level_nr < 0)
153       new_level_nr = 0;
154     if (new_level_nr > leveldir[leveldir_nr].levels - 1)
155       new_level_nr = leveldir[leveldir_nr].levels - 1;
156
157     if (old_level_nr == new_level_nr || !DelayReached(&level_delay, 150))
158       goto out;
159
160     level_nr = new_level_nr;
161
162     DrawTextExt(drawto, gc, SX + 11 * 32, SY + 3 * 32,
163                 int2str(level_nr, 3), FS_BIG, font_color);
164     DrawTextExt(window, gc, SX + 11 * 32, SY + 3 * 32,
165                 int2str(level_nr, 3), FS_BIG, font_color);
166
167     LoadLevel(level_nr);
168     DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS);
169
170     TapeErase();
171     LoadTape(level_nr);
172     DrawCompleteVideoDisplay();
173
174     /* needed because DrawMicroLevel() takes some time */
175     BackToFront();
176     XSync(display, FALSE);
177     DelayReached(&level_delay, 0);      /* reset delay counter */
178   }
179   else if (x == 1 && y >= 3 && y <= 10)
180   {
181     if (button)
182     {
183       if (y != choice)
184       {
185         DrawGraphic(0, y-1, GFX_KUGEL_ROT);
186         DrawGraphic(0, choice - 1, GFX_KUGEL_BLAU);
187         choice = y;
188       }
189     }
190     else
191     {
192       if (y == 3)
193       {
194         game_status = TYPENAME;
195         HandleTypeName(strlen(setup.player_name), 0);
196       }
197       else if (y == 4)
198       {
199         if (num_leveldirs)
200         {
201           game_status = CHOOSELEVEL;
202           SaveLevelSetup();
203           DrawChooseLevel();
204         }
205       }
206       else if (y == 5)
207       {
208         game_status = HALLOFFAME;
209         DrawHallOfFame(-1);
210       }
211       else if (y == 6)
212       {
213         if (leveldir[leveldir_nr].readonly)
214           Request("This level is read only !", REQ_CONFIRM);
215         game_status = LEVELED;
216         DrawLevelEd();
217       }
218       else if (y == 7)
219       {
220         game_status = HELPSCREEN;
221         DrawHelpScreen();
222       }
223       else if (y == 8)
224       {
225         if (setup.autorecord)
226           TapeStartRecording();
227
228         if (options.network)
229           SendToServer_StartPlaying();
230         else
231         {
232           game_status = PLAYING;
233           InitGame();
234         }
235       }
236       else if (y == 9)
237       {
238         game_status = SETUP;
239         DrawSetupScreen();
240       }
241       else if (y == 10)
242       {
243         SaveLevelSetup();
244         if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
245           game_status = EXITGAME;
246       }
247
248       redraw = TRUE;
249     }
250   }
251   BackToFront();
252
253   out:
254
255   if (game_status == MAINMENU)
256     DoAnimation();
257 }
258
259 #define MAX_HELPSCREEN_ELS      10
260 #define HA_NEXT                 -999
261 #define HA_END                  -1000
262
263 static long helpscreen_state;
264 static int helpscreen_step[MAX_HELPSCREEN_ELS];
265 static int helpscreen_frame[MAX_HELPSCREEN_ELS];
266 static int helpscreen_delay[MAX_HELPSCREEN_ELS];
267 static int helpscreen_action[] =
268 {
269   GFX_SPIELER1_DOWN,4,2,
270   GFX_SPIELER1_UP,4,2,
271   GFX_SPIELER1_LEFT,4,2,
272   GFX_SPIELER1_RIGHT,4,2,
273   GFX_SPIELER1_PUSH_LEFT,4,2,
274   GFX_SPIELER1_PUSH_RIGHT,4,2,                                  HA_NEXT,
275   GFX_ERDREICH,1,100,                                           HA_NEXT,
276   GFX_LEERRAUM,1,100,                                           HA_NEXT,
277   GFX_MORAST_LEER,1,100,                                        HA_NEXT,
278   GFX_BETON,1,100,                                              HA_NEXT,
279   GFX_MAUERWERK,1,100,                                          HA_NEXT,
280   GFX_MAUER_R1,3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
281   GFX_MAUER_L1,3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,      HA_NEXT,
282   GFX_UNSICHTBAR,1,100,                                         HA_NEXT,
283   GFX_FELSBODEN,1,100,                                          HA_NEXT,
284   GFX_CHAR_A,30,4, GFX_CHAR_AUSRUF,32,4,                        HA_NEXT,
285   GFX_EDELSTEIN,2,5,                                            HA_NEXT,
286   GFX_DIAMANT,2,5,                                              HA_NEXT,
287   GFX_EDELSTEIN_BD,2,5,                                         HA_NEXT,
288   GFX_EDELSTEIN_GELB,2,5, GFX_EDELSTEIN_ROT,2,5,
289   GFX_EDELSTEIN_LILA,2,5,                                       HA_NEXT,
290   GFX_FELSBROCKEN,4,5,                                          HA_NEXT,
291   GFX_BOMBE,1,50, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10,         HA_NEXT,
292   GFX_KOKOSNUSS,1,50, GFX_CRACKINGNUT,3,1, GFX_EDELSTEIN,1,10,  HA_NEXT,
293   GFX_ERZ_EDEL,1,50, GFX_EXPLOSION,8,1, GFX_EDELSTEIN,1,10,     HA_NEXT,
294   GFX_ERZ_DIAM,1,50, GFX_EXPLOSION,8,1, GFX_DIAMANT,1,10,       HA_NEXT,
295   GFX_ERZ_EDEL_BD,1,50, GFX_EXPLOSION,8,1,GFX_EDELSTEIN_BD,1,10,HA_NEXT,
296   GFX_ERZ_EDEL_GELB,1,50, GFX_EXPLOSION,8,1,
297   GFX_EDELSTEIN_GELB,1,10, GFX_ERZ_EDEL_ROT,1,50,
298   GFX_EXPLOSION,8,1, GFX_EDELSTEIN_ROT,1,10,
299   GFX_ERZ_EDEL_LILA,1,50, GFX_EXPLOSION,8,1,
300   GFX_EDELSTEIN_LILA,1,10,                                      HA_NEXT,
301   GFX_GEBLUBBER,4,4,                                            HA_NEXT,
302   GFX_SCHLUESSEL1,4,25,                                         HA_NEXT,
303   GFX_PFORTE1,4,25,                                             HA_NEXT,
304   GFX_PFORTE1X,4,25,                                            HA_NEXT,
305   GFX_DYNAMIT_AUS,1,100,                                        HA_NEXT,
306   GFX_DYNAMIT,7,6, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10,        HA_NEXT,
307   GFX_DYNABOMB+0,4,3, GFX_DYNABOMB+3,1,3, GFX_DYNABOMB+2,1,3,
308   GFX_DYNABOMB+1,1,3, GFX_DYNABOMB+0,1,3, GFX_EXPLOSION,8,1,
309   GFX_LEERRAUM,1,10,                                            HA_NEXT,
310   GFX_DYNABOMB_NR,1,100,                                        HA_NEXT,
311   GFX_DYNABOMB_SZ,1,100,                                        HA_NEXT,
312   GFX_FLIEGER+4,1,3, GFX_FLIEGER+0,1,3, GFX_FLIEGER+4,1,3,
313   GFX_FLIEGER+5,1,3, GFX_FLIEGER+1,1,3, GFX_FLIEGER+5,1,3,
314   GFX_FLIEGER+6,1,3, GFX_FLIEGER+2,1,3, GFX_FLIEGER+6,1,3,
315   GFX_FLIEGER+7,1,3, GFX_FLIEGER+3,1,3, GFX_FLIEGER+7,1,3,      HA_NEXT,
316   GFX_KAEFER+4,1,1, GFX_KAEFER+0,1,1, GFX_KAEFER+4,1,1,
317   GFX_KAEFER+5,1,1, GFX_KAEFER+1,1,1, GFX_KAEFER+5,1,1,
318   GFX_KAEFER+6,1,1, GFX_KAEFER+2,1,1, GFX_KAEFER+6,1,1,
319   GFX_KAEFER+7,1,1, GFX_KAEFER+3,1,1, GFX_KAEFER+7,1,1,         HA_NEXT,
320   GFX_BUTTERFLY,2,2,                                            HA_NEXT,
321   GFX_FIREFLY,2,2,                                              HA_NEXT,
322   GFX_PACMAN+0,1,3, GFX_PACMAN+4,1,2, GFX_PACMAN+0,1,3,
323   GFX_PACMAN+1,1,3, GFX_PACMAN+5,1,2, GFX_PACMAN+1,1,3,
324   GFX_PACMAN+2,1,3, GFX_PACMAN+6,1,2, GFX_PACMAN+2,1,3,
325   GFX_PACMAN+3,1,3, GFX_PACMAN+7,1,2, GFX_PACMAN+3,1,3,         HA_NEXT,
326   GFX_MAMPFER+0,4,1, GFX_MAMPFER+3,1,1, GFX_MAMPFER+2,1,1,
327   GFX_MAMPFER+1,1,1, GFX_MAMPFER+0,1,1,                         HA_NEXT,
328   GFX_MAMPFER2+0,4,1, GFX_MAMPFER2+3,1,1, GFX_MAMPFER2+2,1,1,
329   GFX_MAMPFER2+1,1,1, GFX_MAMPFER2+0,1,1,                       HA_NEXT,
330   GFX_ROBOT+0,4,1, GFX_ROBOT+3,1,1, GFX_ROBOT+2,1,1,
331   GFX_ROBOT+1,1,1, GFX_ROBOT+0,1,1,                             HA_NEXT,
332   GFX_MAULWURF_DOWN,4,2,
333   GFX_MAULWURF_UP,4,2,
334   GFX_MAULWURF_LEFT,4,2,
335   GFX_MAULWURF_RIGHT,4,2,                                       HA_NEXT,
336   GFX_PINGUIN_DOWN,4,2,
337   GFX_PINGUIN_UP,4,2,
338   GFX_PINGUIN_LEFT,4,2,
339   GFX_PINGUIN_RIGHT,4,2,                                        HA_NEXT,
340   GFX_SCHWEIN_DOWN,4,2,
341   GFX_SCHWEIN_UP,4,2,
342   GFX_SCHWEIN_LEFT,4,2,
343   GFX_SCHWEIN_RIGHT,4,2,                                        HA_NEXT,
344   GFX_DRACHE_DOWN,4,2,
345   GFX_DRACHE_UP,4,2,
346   GFX_DRACHE_LEFT,4,2,
347   GFX_DRACHE_RIGHT,4,2,                                         HA_NEXT,
348   GFX_SONDE_START,8,1,                                          HA_NEXT,
349   GFX_ABLENK,4,1,                                               HA_NEXT,
350   GFX_BIRNE_AUS,1,25, GFX_BIRNE_EIN,1,25,                       HA_NEXT,
351   GFX_ZEIT_VOLL,1,25, GFX_ZEIT_LEER,1,25,                       HA_NEXT,
352   GFX_TROPFEN,1,25, GFX_AMOEBING,4,1, GFX_AMOEBE_LEBT,1,10,     HA_NEXT,
353   GFX_AMOEBE_TOT+2,2,50, GFX_AMOEBE_TOT,2,50,                   HA_NEXT,
354   GFX_AMOEBE_LEBT,4,40,                                         HA_NEXT,
355   GFX_AMOEBE_LEBT,1,10, GFX_AMOEBING,4,2,                       HA_NEXT,
356   GFX_AMOEBE_LEBT,1,25, GFX_AMOEBE_TOT,1,25, GFX_EXPLOSION,8,1,
357   GFX_DIAMANT,1,10,                                             HA_NEXT,
358   GFX_LIFE,1,100,                                               HA_NEXT,
359   GFX_LIFE_ASYNC,1,100,                                         HA_NEXT,
360   GFX_SIEB_LEER,4,2,                                            HA_NEXT,
361   GFX_SIEB2_LEER,4,2,                                           HA_NEXT,
362   GFX_AUSGANG_ZU,1,100, GFX_AUSGANG_ACT,4,2,
363   GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2,
364   GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2,                 HA_NEXT,
365   GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2,
366   GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2,                 HA_NEXT,
367   HA_END
368 };
369 static char *helpscreen_eltext[][2] =
370 {
371  {"THE HERO:",                          "(Is _this_ guy good old Rockford?)"},
372  {"Normal sand:",                       "You can dig through it"},
373  {"Empty field:",                       "You can walk through it"},
374  {"Quicksand: You cannot pass it,",     "but rocks can fall though it"},
375  {"Massive Wall:",                      "Nothing can go through it"},
376  {"Normal Wall: You can't go through",  "it, but you can bomb it away"},
377  {"Growing Wall: Grows to the left or", "right if there is an empty field"},
378  {"Invisible Wall: Behaves like normal","wall, but is invisible"},
379  {"Old Wall: Like normal wall, but",    "some things can fall down from it"},
380  {"Letter Wall: Looks like a letter,",  "behaves like a normal wall"},
381  {"Emerald: You must collect enough of","them to finish a level"},
382  {"Diamond: Counts as 3 emeralds, but", "can be destroyed by rocks"},
383  {"Diamond (BD style): Counts like one","emerald and behaves a bit different"},
384  {"Colorful Gems:",                     "Seem to behave like Emeralds"},
385  {"Rock: Smashes several things;",      "Can be moved by the player"},
386  {"Bomb: You can move it, but be",      "careful when dropping it"},
387  {"Nut: Throw a rock on it to open it;","Each nut contains an emerald"},
388  {"Wall with an emerald inside:",       "Bomb the wall away to get it"},
389  {"Wall with a diamond inside:",        "Bomb the wall away to get it"},
390  {"Wall with BD style diamond inside:", "Bomb the wall away to get it"},
391  {"Wall with colorful gem inside:",     "Bomb the wall away to get it"},
392  {"Acid: Things that fall in are gone", "forever (including our hero)"},
393  {"Key: Opens the door that has the",   "same color (red/yellow/green/blue)"},
394  {"Door: Can be opened by the key",     "with the same color"},
395  {"Door: You have to find out the",     "right color of the key for it"},
396  {"Dynamite: Collect it and use it to", "destroy walls or kill enemies"},
397  {"Dynamite: This one explodes after",  "a few seconds"},
398  {"Dyna Bomb: Explodes in 4 directions","with variable explosion size"},
399  {"Dyna Bomb: Increases the number of", "dyna bombs available at a time"},
400  {"Dyna Bomb: Increases the size of",   "explosion of dyna bombs"},
401  {"Spaceship: Moves at the left side",  "of walls; don't touch it!"},
402  {"Bug: Moves at the right side",       "of walls; don't touch it!"},
403  {"Butterfly: Moves at the right side", "of walls; don't touch it!"},
404  {"Firefly: Moves at the left side",    "of walls; don't touch it!"},
405  {"Pacman: Eats the amoeba and you,",   "if you're not careful"},
406  {"Cruncher: Eats diamonds and you,",   "if you're not careful"},
407  {"Cruncher (BD style):",               "Eats almost everything"},
408  {"Robot: Tries to kill the player",    ""},
409  {"The mole: You must guide him savely","to the exit; he will follow you"},
410  {"The penguin: Guide him to the exit,","but keep him away from monsters!"},
411  {"The Pig: Harmless, but eats all",    "gems it can get"},
412  {"The Dragon: Breathes fire,",         "especially to some monsters"},
413  {"Sonde: Follows you everywhere;",     "harmless, but may block your way"},
414  {"Magic Wheel: Touch it to get rid of","the robots for some seconds"},
415  {"Light Bulb: All of them must be",    "switched on to finish a level"},
416  {"Extra Time Orb: Adds some seconds",  "to the time available for the level"},
417  {"Amoeba Drop: Grows to an amoeba on", "the ground - don't touch it"},
418  {"Dead Amoeba: Does not grow, but",    "can still kill bugs and spaceships"},
419  {"Normal Amoeba: Grows through empty", "fields, sand and quicksand"},
420  {"Dropping Amoeba: This one makes",    "drops that grow to a new amoeba"},
421  {"Living Amoeba (BD style): Contains", "other element, when surrounded"},
422  {"Game Of Life: Behaves like the well","known 'Game Of Life' (2333 style)"},
423  {"Biomaze: A bit like the 'Game Of",   "Life', but builds crazy mazes"},
424  {"Magic Wall: Changes rocks, emeralds","and diamonds when they pass it"},
425  {"Magic Wall (BD style):",             "Changes rocks and BD style diamonds"},
426  {"Exit door: Opens if you have enough","emeralds to finish the level"},
427  {"Open exit door: Enter here to leave","the level and exit the actual game"},
428 };
429 static int num_helpscreen_els = sizeof(helpscreen_eltext)/(2*sizeof(char *));
430
431 static char *helpscreen_music[][3] =
432 {
433   { "Alchemy",                  "Ian Boddy",            "Drive" },
434   { "The Chase",                "Propaganda",           "A Secret Wish" },
435   { "Network 23",               "Tangerine Dream",      "Exit" },
436   { "Czardasz",                 "Robert Pieculewicz",   "Czardasz" },
437   { "21st Century Common Man",  "Tangerine Dream",      "Tyger" },
438   { "Voyager",                  "The Alan Parsons Project","Pyramid" },
439   { "Twilight Painter",         "Tangerine Dream",      "Heartbreakers" }
440 };
441 static int helpscreen_musicpos;
442
443 void DrawHelpScreenElAction(int start)
444 {
445   int i = 0, j = 0;
446   int frame, graphic;
447   int xstart = SX+16, ystart = SY+64+2*32, ystep = TILEY+4;
448
449   while(helpscreen_action[j] != HA_END)
450   {
451     if (i>=start+MAX_HELPSCREEN_ELS || i>=num_helpscreen_els)
452       break;
453     else if (i<start || helpscreen_delay[i-start])
454     {
455       if (i>=start && helpscreen_delay[i-start])
456         helpscreen_delay[i-start]--;
457
458       while(helpscreen_action[j] != HA_NEXT)
459         j++;
460       j++;
461       i++;
462       continue;
463     }
464
465     j += 3*helpscreen_step[i-start];
466     graphic = helpscreen_action[j++];
467
468     if (helpscreen_frame[i-start])
469     {
470       frame = helpscreen_action[j++] - helpscreen_frame[i-start];
471       helpscreen_frame[i-start]--;
472     }
473     else
474     {
475       frame = 0;
476       helpscreen_frame[i-start] = helpscreen_action[j++]-1;
477     }
478
479     helpscreen_delay[i-start] = helpscreen_action[j++] - 1;
480
481     if (helpscreen_action[j] == HA_NEXT)
482     {
483       if (!helpscreen_frame[i-start])
484         helpscreen_step[i-start] = 0;
485     }
486     else
487     {
488       if (!helpscreen_frame[i-start])
489         helpscreen_step[i-start]++;
490       while(helpscreen_action[j] != HA_NEXT)
491         j++;
492     }
493     j++;
494
495     DrawGraphicExt(drawto, gc, xstart, ystart+(i-start)*ystep, graphic+frame);
496     i++;
497   }
498
499   for(i=2;i<16;i++)
500   {
501     MarkTileDirty(0,i);
502     MarkTileDirty(1,i);
503   }
504 }
505
506 void DrawHelpScreenElText(int start)
507 {
508   int i;
509   int xstart = SX + 56, ystart = SY + 65 + 2 * 32, ystep = TILEY + 4;
510   int ybottom = SYSIZE - 20;
511
512   ClearWindow();
513   DrawHeadline();
514
515   DrawTextFCentered(100, FC_GREEN, "The game elements:");
516
517   for(i=start; i < start + MAX_HELPSCREEN_ELS && i < num_helpscreen_els; i++)
518   {
519     DrawText(xstart,
520              ystart + (i - start) * ystep + (*helpscreen_eltext[i][1] ? 0 : 8),
521              helpscreen_eltext[i][0], FS_SMALL, FC_YELLOW);
522     DrawText(xstart, ystart + (i - start) * ystep + 16,
523              helpscreen_eltext[i][1], FS_SMALL, FC_YELLOW);
524   }
525
526   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
527 }
528
529 void DrawHelpScreenMusicText(int num)
530 {
531   int ystart = 150, ystep = 30;
532   int ybottom = SYSIZE - 20;
533
534   FadeSounds();
535   ClearWindow();
536   DrawHeadline();
537
538   DrawTextFCentered(100, FC_GREEN, "The game background music loops:");
539
540   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
541                     "Excerpt from");
542   DrawTextFCentered(ystart + 1 * ystep, FC_RED, "\"%s\"",
543                     helpscreen_music[num][0]);
544   DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW,
545                     "by");
546   DrawTextFCentered(ystart + 3 * ystep, FC_RED,
547                     "%s", helpscreen_music[num][1]);
548   DrawTextFCentered(ystart + 4 * ystep, FC_YELLOW,
549                     "from the album");
550   DrawTextFCentered(ystart + 5 * ystep, FC_RED, "\"%s\"",
551                     helpscreen_music[num][2]);
552
553   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
554
555   PlaySoundLoop(background_loop[num]);
556 }
557
558 void DrawHelpScreenCreditsText()
559 {
560   int ystart = 150, ystep = 30;
561   int ybottom = SYSIZE - 20;
562
563   FadeSounds();
564   ClearWindow();
565   DrawHeadline();
566
567   DrawTextFCentered(100, FC_GREEN,
568                     "Credits:");
569   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
570                     "DOS/Windows port of the game:");
571   DrawTextFCentered(ystart + 1 * ystep, FC_RED,
572                     "Guido Schulz");
573   DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW,
574                     "Additional toons:");
575   DrawTextFCentered(ystart + 3 * ystep, FC_RED,
576                     "Karl Hörnell");
577   DrawTextFCentered(ystart + 5 * ystep, FC_YELLOW,
578                     "...and many thanks to all contributors");
579   DrawTextFCentered(ystart + 6 * ystep, FC_YELLOW,
580                     "of new levels!");
581
582   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
583 }
584
585 void DrawHelpScreenContactText()
586 {
587   int ystart = 150, ystep = 30;
588   int ybottom = SYSIZE - 20;
589
590   ClearWindow();
591   DrawHeadline();
592
593   DrawTextFCentered(100, FC_GREEN, "Program information:");
594
595   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
596                     "This game is Freeware!");
597   DrawTextFCentered(ystart + 1 * ystep, FC_YELLOW,
598                     "If you like it, send e-mail to:");
599   DrawTextFCentered(ystart + 2 * ystep, FC_RED,
600                     "aeglos@valinor.owl.de");
601   DrawTextFCentered(ystart + 3 * ystep, FC_YELLOW,
602                     "or SnailMail to:");
603   DrawTextFCentered(ystart + 4 * ystep + 0, FC_RED,
604                     "Holger Schemel");
605   DrawTextFCentered(ystart + 4 * ystep + 20, FC_RED,
606                     "Oststrasse 11a");
607   DrawTextFCentered(ystart + 4 * ystep + 40, FC_RED,
608                     "33604 Bielefeld");
609   DrawTextFCentered(ystart + 4 * ystep + 60, FC_RED,
610                     "Germany");
611
612   DrawTextFCentered(ystart + 7 * ystep, FC_YELLOW,
613                     "If you have created new levels,");
614   DrawTextFCentered(ystart + 8 * ystep, FC_YELLOW,
615                     "send them to me to include them!");
616   DrawTextFCentered(ystart + 9 * ystep, FC_YELLOW,
617                     ":-)");
618
619   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for main menu");
620 }
621
622 void DrawHelpScreen()
623 {
624   int i;
625
626   CloseDoor(DOOR_CLOSE_2);
627
628   for(i=0;i<MAX_HELPSCREEN_ELS;i++)
629     helpscreen_step[i] = helpscreen_frame[i] = helpscreen_delay[i] = 0;
630   helpscreen_musicpos = 0;
631   helpscreen_state = 0;
632   DrawHelpScreenElText(0);
633   DrawHelpScreenElAction(0);
634
635   FadeToFront();
636   InitAnimation();
637   PlaySoundLoop(SND_RHYTHMLOOP);
638 }
639
640 void HandleHelpScreen(int button)
641 {
642   static long hs_delay = 0;
643   int num_helpscreen_els_pages =
644     (num_helpscreen_els + MAX_HELPSCREEN_ELS-1) / MAX_HELPSCREEN_ELS;
645   int button_released = !button;
646   int i;
647
648   if (button_released)
649   {
650     if (helpscreen_state < num_helpscreen_els_pages - 1)
651     {
652       for(i=0;i<MAX_HELPSCREEN_ELS;i++)
653         helpscreen_step[i] = helpscreen_frame[i] = helpscreen_delay[i] = 0;
654       helpscreen_state++;
655       DrawHelpScreenElText(helpscreen_state*MAX_HELPSCREEN_ELS);
656       DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
657     }
658     else if (helpscreen_state < num_helpscreen_els_pages + num_bg_loops - 1)
659     {
660       helpscreen_state++;
661       DrawHelpScreenMusicText(helpscreen_state - num_helpscreen_els_pages);
662     }
663     else if (helpscreen_state == num_helpscreen_els_pages + num_bg_loops - 1)
664     {
665       helpscreen_state++;
666       DrawHelpScreenCreditsText();
667     }
668     else if (helpscreen_state == num_helpscreen_els_pages + num_bg_loops)
669     {
670       helpscreen_state++;
671       DrawHelpScreenContactText();
672     }
673     else
674     {
675       FadeSounds();
676       DrawMainMenu();
677       game_status = MAINMENU;
678     }
679   }
680   else
681   {
682     if (DelayReached(&hs_delay,GAME_FRAME_DELAY * 2))
683     {
684       if (helpscreen_state<num_helpscreen_els_pages)
685         DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
686     }
687     DoAnimation();
688   }
689
690   BackToFront();
691 }
692
693 void HandleTypeName(int newxpos, KeySym key)
694 {
695   static int xpos = 0, ypos = 2;
696   unsigned char ascii;
697
698   if (newxpos)
699   {
700     xpos = newxpos;
701     DrawText(SX+6*32, SY+ypos*32, setup.player_name, FS_BIG, FC_YELLOW);
702     DrawGraphic(xpos+6,ypos,GFX_KUGEL_ROT);
703     return;
704   }
705
706 #ifndef MSDOS
707   if ((key>=XK_A && key<=XK_Z) || (key>=XK_a && key<=XK_z && 
708       xpos<MAX_NAMELEN-1))
709   {
710     if (key>=XK_A && key<=XK_Z)
711       ascii = 'A'+(char)(key-XK_A);
712     if (key>=XK_a && key<=XK_z)
713       ascii = 'a'+(char)(key-XK_a);
714
715     setup.player_name[xpos] = ascii;
716     setup.player_name[xpos+1] = 0;
717     xpos++;
718     DrawTextExt(drawto,gc,SX+6*32,SY+ypos*32,
719                 setup.player_name,FS_BIG,FC_YELLOW);
720     DrawTextExt(window,gc,SX+6*32,SY+ypos*32,
721                 setup.player_name,FS_BIG,FC_YELLOW);
722     DrawGraphic(xpos+6,ypos,GFX_KUGEL_ROT);
723   }
724 #else
725   if ((ascii = get_ascii(key)) && xpos<MAX_NAMELEN-1)
726   {
727     setup.player_name[xpos] = ascii;
728     setup.player_name[xpos+1] = 0;
729     xpos++;
730     DrawTextExt(drawto,gc,SX+6*32,SY+ypos*32,
731                 setup.player_name,FS_BIG,FC_YELLOW);
732     DrawTextExt(window,gc,SX+6*32,SY+ypos*32,
733                 setup.player_name,FS_BIG,FC_YELLOW);
734     DrawGraphic(xpos+6,ypos,GFX_KUGEL_ROT);
735   }
736 #endif
737   else if ((key==XK_Delete || key==XK_BackSpace) && xpos>0)
738   {
739     xpos--;
740     setup.player_name[xpos] = 0;
741     DrawGraphic(xpos+6,ypos,GFX_KUGEL_ROT);
742     DrawGraphic(xpos+7,ypos,GFX_LEERRAUM);
743   }
744   else if (key==XK_Return && xpos>0)
745   {
746     DrawText(SX+6*32,SY+ypos*32,setup.player_name,FS_BIG,FC_RED);
747     DrawGraphic(xpos+6,ypos,GFX_LEERRAUM);
748
749     SaveSetup();
750     game_status = MAINMENU;
751   }
752
753   BackToFront();
754 }
755
756 void DrawChooseLevel()
757 {
758   CloseDoor(DOOR_CLOSE_2);
759
760   FadeToFront();
761   InitAnimation();
762   HandleChooseLevel(0,0, 0,0, MB_MENU_INITIALIZE);
763 }
764
765 static void drawChooseLevelList(int first_entry, int num_page_entries)
766 {
767   int i;
768   char buffer[SCR_FIELDX];
769
770   ClearWindow();
771   DrawText(SX, SY, "Level Directories", FS_BIG, FC_GREEN);
772
773   for(i=0; i<num_page_entries; i++)
774   {
775     strncpy(buffer, leveldir[first_entry + i].name , SCR_FIELDX - 1);
776     buffer[SCR_FIELDX - 1] = '\0';
777     DrawText(SX + 32, SY + (i + 2) * 32, buffer, FS_BIG, FC_YELLOW);
778     DrawGraphic(0, i + 2, GFX_KUGEL_BLAU);
779   }
780
781   if (first_entry > 0)
782     DrawGraphic(0, 1, GFX_PFEIL_O);
783
784   if (first_entry + num_page_entries < num_leveldirs)
785     DrawGraphic(0, MAX_VISIBLE_ENTRIES + 1, GFX_PFEIL_U);
786 }
787
788 static void drawChooseLevelInfo(int leveldir_nr)
789 {
790   XFillRectangle(display, drawto, gc, SX + 32, SY + 32, SXSIZE - 32, 32);
791   DrawTextFCentered(40, FC_RED, "%3d levels (%s)",
792                     leveldir[leveldir_nr].levels,
793                     leveldir[leveldir_nr].readonly ? "readonly" : "writable");
794 }
795
796 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
797 {
798   static int choice = 3;
799   static int first_entry = 0;
800   static long choose_delay = 0;
801   static int redraw = TRUE;
802   int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
803   int num_page_entries;
804
805   if (num_leveldirs <= MAX_VISIBLE_ENTRIES)
806     num_page_entries = num_leveldirs;
807   else
808     num_page_entries = MAX_VISIBLE_ENTRIES - 1;
809
810   if (button == MB_MENU_INITIALIZE)
811   {
812     redraw = TRUE;
813     choice = leveldir_nr + 3 - first_entry;
814
815     if (choice > num_page_entries + 2)
816     {
817       choice = num_page_entries + 2;
818       first_entry = num_leveldirs - num_page_entries;
819     }
820
821     drawChooseLevelList(first_entry, num_page_entries);
822     drawChooseLevelInfo(leveldir_nr);
823   }
824
825   if (redraw)
826   {
827     DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
828     redraw = FALSE;
829   }
830
831   if (button == MB_MENU_INITIALIZE)
832     return;
833
834   if (dx || dy)
835   {
836     if (dy)
837     {
838       x = 1;
839       y = choice + dy;
840     }
841     else
842       x = y = 0;
843   }
844
845   if (x == 1 && y == 2)
846   {
847     if (first_entry > 0 &&
848         (dy || DelayReached(&choose_delay, 150)))
849     {
850       first_entry--;
851       drawChooseLevelList(first_entry, num_page_entries);
852       drawChooseLevelInfo(first_entry);
853       DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
854       return;
855     }
856   }
857   else if (x == 1 && y > num_page_entries + 2)
858   {
859     if (first_entry + num_page_entries < num_leveldirs &&
860         (dy || DelayReached(&choose_delay, 150)))
861     {
862       first_entry++;
863       drawChooseLevelList(first_entry, num_page_entries);
864       drawChooseLevelInfo(first_entry + num_page_entries - 1);
865       DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
866       return;
867     }
868   }
869
870   if (!mx && !my && !dx && !dy)
871   {
872     x = 1;
873     y = choice;
874   }
875
876   if (x == 1 && y >= 3 && y <= num_page_entries + 2)
877   {
878     if (button)
879     {
880       if (y != choice)
881       {
882         DrawGraphic(0, y - 1, GFX_KUGEL_ROT);
883         DrawGraphic(0, choice - 1, GFX_KUGEL_BLAU);
884         drawChooseLevelInfo(first_entry + y - 3);
885         choice = y;
886       }
887     }
888     else
889     {
890       leveldir_nr = first_entry + y - 3;
891       level_nr =
892         getLastPlayedLevelOfLevelSeries(leveldir[leveldir_nr].filename);
893
894       SaveLevelSetup();
895       TapeErase();
896
897       game_status = MAINMENU;
898       DrawMainMenu();
899       redraw = TRUE;
900     }
901   }
902
903   BackToFront();
904
905   if (game_status == CHOOSELEVEL)
906     DoAnimation();
907 }
908
909 void DrawHallOfFame(int highlight_position)
910 {
911   int i;
912
913   CloseDoor(DOOR_CLOSE_2);
914
915   if (highlight_position < 0) 
916     LoadScore(level_nr);
917
918   ClearWindow();
919
920   DrawText(SX + 80, SY + 8, "Hall Of Fame", FS_BIG, FC_YELLOW);
921   DrawTextFCentered(46, FC_RED, "HighScores of Level %d", level_nr);
922
923   for(i=0; i<MAX_VISIBLE_ENTRIES; i++)
924   {
925     DrawText(SX, SY + 64 + i * 32, ".................", FS_BIG,
926              (i == highlight_position ? FC_RED : FC_GREEN));
927     DrawText(SX, SY + 64 + i * 32, highscore[i].Name, FS_BIG,
928              (i == highlight_position ? FC_RED : FC_GREEN));
929     DrawText(SX + 12 * 32, SY + 64 + i * 32,
930              int2str(highscore[i].Score, 5), FS_BIG,
931              (i == highlight_position ? FC_RED : FC_GREEN));
932   }
933
934   FadeToFront();
935   InitAnimation();
936   PlaySound(SND_HALLOFFAME);
937 }
938
939 void HandleHallOfFame(int button)
940 {
941   int button_released = !button;
942
943   if (button_released)
944   {
945     FadeSound(SND_HALLOFFAME);
946     game_status = MAINMENU;
947     DrawMainMenu();
948     BackToFront();
949   }
950   else
951     DoAnimation();
952 }
953
954 void DrawSetupScreen()
955 {
956   int i;
957   static struct setup
958   {
959     boolean *value;
960     char *text;
961   } setup_info[] =
962   {
963     { &setup.sound,             "Sound:",       },
964     { &setup.sound_loops,       " Sound Loops:" },
965     { &setup.sound_music,       " Game Music:"  },
966     { &setup.toons,             "Toons:"        },
967     { &setup.double_buffering,  "Buffered gfx:" },
968     { &setup.scroll_delay,      "Scroll Delay:" },
969     { &setup.soft_scrolling,    "Soft Scroll.:" },
970     { &setup.fading,            "Fading:"       },
971     { &setup.quick_doors,       "Quick Doors:"  },
972     { &setup.autorecord,        "Auto-Record:"  },
973     { &setup.team_mode,         "Team-Mode:"    },
974     { NULL,                     "Input Devices" },
975     { NULL,                     ""              },
976     { NULL,                     "Exit"          },
977     { NULL,                     "Save and exit" }
978   };
979
980   CloseDoor(DOOR_CLOSE_2);
981   ClearWindow();
982   DrawText(SX+16, SY+16, "SETUP",FS_BIG,FC_YELLOW);
983
984   for(i=SETUP_SCREEN_POS_START;i<=SETUP_SCREEN_POS_END;i++)
985   {
986     int base = i - SETUP_SCREEN_POS_START;
987
988     if (!(i >= SETUP_SCREEN_POS_EMPTY1 && i <= SETUP_SCREEN_POS_EMPTY2))
989     {
990       DrawGraphic(0,i,GFX_KUGEL_BLAU);
991       DrawText(SX+32,SY+i*32, setup_info[base].text, FS_BIG,FC_GREEN);
992     }
993
994     if (setup_info[base].value)
995     {
996       int setting_value = *setup_info[base].value;
997
998       DrawText(SX+14*32, SY+i*32, (setting_value ? "on" : "off"),
999                FS_BIG, (setting_value ? FC_YELLOW : FC_BLUE));
1000     }
1001   }
1002
1003   FadeToFront();
1004   InitAnimation();
1005   HandleSetupScreen(0,0,0,0,MB_MENU_INITIALIZE);
1006 }
1007
1008 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
1009 {
1010   static int choice = 3;
1011   static int redraw = TRUE;
1012   int x = (mx+32-SX)/32, y = (my+32-SY)/32;
1013   int pos_start  = SETUP_SCREEN_POS_START  + 1;
1014   int pos_empty1 = SETUP_SCREEN_POS_EMPTY1 + 1;
1015   int pos_empty2 = SETUP_SCREEN_POS_EMPTY2 + 1;
1016   int pos_end    = SETUP_SCREEN_POS_END    + 1;
1017
1018   if (button == MB_MENU_INITIALIZE)
1019     redraw = TRUE;
1020
1021   if (redraw)
1022   {
1023     DrawGraphic(0,choice-1,GFX_KUGEL_ROT);
1024     redraw = FALSE;
1025   }
1026
1027   if (button == MB_MENU_INITIALIZE)
1028     return;
1029
1030   if (dx || dy)
1031   {
1032     if (dy)
1033     {
1034       x = 1;
1035       y = choice+dy;
1036     }
1037     else
1038       x = y = 0;
1039
1040     if (y >= pos_empty1 && y <= pos_empty2)
1041       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
1042
1043     if (y < pos_start)
1044       y = pos_start;
1045     else if (y > pos_end)
1046       y = pos_end;
1047   }
1048
1049   if (!mx && !my && !dx && !dy)
1050   {
1051     x = 1;
1052     y = choice;
1053   }
1054
1055   if (x==1 && y >= pos_start && y <= pos_end &&
1056       !(y >= pos_empty1 && y <= pos_empty2))
1057   {
1058     if (button)
1059     {
1060       if (y!=choice)
1061       {
1062         DrawGraphic(0,y-1,GFX_KUGEL_ROT);
1063         DrawGraphic(0,choice-1,GFX_KUGEL_BLAU);
1064       }
1065       choice = y;
1066     }
1067     else
1068     {
1069       int yy = y-1;
1070
1071       if (y==3 && sound_status==SOUND_AVAILABLE)
1072       {
1073         if (setup.sound)
1074         {
1075           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1076           DrawText(SX+14*32, SY+(yy+1)*32,"off",FS_BIG,FC_BLUE);
1077           DrawText(SX+14*32, SY+(yy+2)*32,"off",FS_BIG,FC_BLUE);
1078           setup.sound_loops = FALSE;
1079           setup.sound_music = FALSE;
1080         }
1081         else
1082           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1083         setup.sound = !setup.sound;
1084       }
1085       else if (y==4 && sound_loops_allowed)
1086       {
1087         if (setup.sound_loops)
1088           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1089         else
1090         {
1091           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1092           DrawText(SX+14*32, SY+(yy-1)*32,"on ",FS_BIG,FC_YELLOW);
1093           setup.sound = TRUE;
1094         }
1095         setup.sound_loops = !setup.sound_loops;
1096       }
1097       else if (y==5 && sound_loops_allowed)
1098       {
1099         if (setup.sound_music)
1100           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1101         else
1102         {
1103           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1104           DrawText(SX+14*32, SY+(yy-2)*32,"on ",FS_BIG,FC_YELLOW);
1105           setup.sound = TRUE;
1106         }
1107         setup.sound_music = !setup.sound_music;
1108       }
1109       else if (y==6)
1110       {
1111         if (setup.toons)
1112           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1113         else
1114           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1115         setup.toons = !setup.toons;
1116       }
1117       else if (y==7)
1118       {
1119         if (setup.double_buffering)
1120           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1121         else
1122           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1123         setup.double_buffering = !setup.double_buffering;
1124         setup.direct_draw = !setup.double_buffering;
1125       }
1126       else if (y==8)
1127       {
1128         if (setup.scroll_delay)
1129           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1130         else
1131           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1132         setup.scroll_delay = !setup.scroll_delay;
1133       }
1134       else if (y==9)
1135       {
1136         if (setup.soft_scrolling)
1137           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1138         else
1139           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1140         setup.soft_scrolling = !setup.soft_scrolling;
1141       }
1142       else if (y==10)
1143       {
1144         if (setup.fading)
1145           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1146         else
1147           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1148         setup.fading = !setup.fading;
1149       }
1150       else if (y==11)
1151       {
1152         if (setup.quick_doors)
1153           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1154         else
1155           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1156         setup.quick_doors = !setup.quick_doors;
1157       }
1158       else if (y==12)
1159       {
1160         if (setup.autorecord)
1161           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1162         else
1163           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1164         setup.autorecord = !setup.autorecord;
1165       }
1166       else if (y==13)
1167       {
1168         if (setup.team_mode)
1169           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1170         else
1171           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1172         setup.team_mode = !setup.team_mode;
1173       }
1174       else if (y==14)
1175       {
1176         game_status = SETUPINPUT;
1177         DrawSetupInputScreen();
1178         redraw = TRUE;
1179       }
1180       else if (y==pos_end-1 || y==pos_end)
1181       {
1182         if (y==pos_end)
1183         {
1184           SaveSetup();
1185
1186           /*
1187           SaveJoystickData();
1188           */
1189
1190         }
1191
1192         game_status = MAINMENU;
1193         DrawMainMenu();
1194         redraw = TRUE;
1195       }
1196     }
1197   }
1198   BackToFront();
1199
1200   if (game_status==SETUP)
1201     DoAnimation();
1202 }
1203
1204 void DrawSetupInputScreen()
1205 {
1206   ClearWindow();
1207   DrawText(SX+16, SY+16, "SETUP INPUT", FS_BIG, FC_YELLOW);
1208
1209   DrawGraphic(0, 2, GFX_KUGEL_BLAU);
1210   DrawGraphic(0, 3, GFX_KUGEL_BLAU);
1211   DrawGraphic(0, 4, GFX_KUGEL_BLAU);
1212   DrawGraphic(0, 15, GFX_KUGEL_BLAU);
1213   DrawGraphic(10, 2, GFX_PFEIL_L);
1214   DrawGraphic(12, 2, GFX_PFEIL_R);
1215
1216   DrawText(SX+32, SY+2*32, "Player:", FS_BIG, FC_GREEN);
1217   DrawText(SX+32, SY+3*32, "Device:", FS_BIG, FC_GREEN);
1218   DrawText(SX+32, SY+15*32, "Exit", FS_BIG, FC_GREEN);
1219
1220   FadeToFront();
1221   InitAnimation();
1222   HandleSetupInputScreen(0,0,0,0,MB_MENU_INITIALIZE);
1223 }
1224
1225 static int getJoystickNrFromDeviceName(char *device_name)
1226 {
1227   char c;
1228   int joystick_nr = 0;
1229
1230   if (device_name == NULL || device_name[0] == '\0')
1231     return 0;
1232
1233   c = device_name[strlen(device_name) - 1];
1234
1235   if (c >= '0' && c <= '9')
1236     joystick_nr = (int)(c - '0');
1237
1238   if (joystick_nr < 0 || joystick_nr >= MAX_PLAYERS)
1239     joystick_nr = 0;
1240
1241   return joystick_nr;
1242 }
1243
1244 static void setJoystickDeviceToNr(char *device_name, int device_nr)
1245 {
1246   if (device_name == NULL)
1247     return;
1248
1249   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
1250     device_nr = 0;
1251
1252   if (strlen(device_name) > 1)
1253   {
1254     char c1 = device_name[strlen(device_name) - 1];
1255     char c2 = device_name[strlen(device_name) - 2];
1256
1257     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
1258       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
1259   }
1260   else
1261     strncpy(device_name, joystick_device_name[device_nr], strlen(device_name));
1262 }
1263
1264 static void drawPlayerSetupInputInfo(int player_nr)
1265 {
1266   int i;
1267   static struct SetupKeyboardInfo custom_key;
1268   static struct
1269   {
1270     KeySym *keysym;
1271     char *text;
1272   } custom[] =
1273   {
1274     { &custom_key.left,  "Joystick Left"  },
1275     { &custom_key.right, "Joystick Right" },
1276     { &custom_key.up,    "Joystick Up"    },
1277     { &custom_key.down,  "Joystick Down"  },
1278     { &custom_key.snap,  "Button 1"       },
1279     { &custom_key.bomb,  "Button 2"       }
1280   };
1281   static char *joystick_name[MAX_PLAYERS] =
1282   {
1283     "Joystick1",
1284     "Joystick2",
1285     "Joystick3",
1286     "Joystick4"
1287   };
1288
1289   custom_key = setup.input[player_nr].key;
1290
1291   DrawText(SX+11*32, SY+2*32, int2str(player_nr + 1, 1), FS_BIG, FC_RED);
1292   DrawGraphic(8, 2, GFX_SPIELER1 + player_nr);
1293
1294   if (setup.input[player_nr].use_joystick)
1295   {
1296     char *device_name = setup.input[player_nr].joy.device_name;
1297
1298     DrawText(SX+8*32, SY+3*32,
1299              joystick_name[getJoystickNrFromDeviceName(device_name)],
1300              FS_BIG, FC_YELLOW);
1301     DrawText(SX+32, SY+4*32, "Calibrate", FS_BIG, FC_GREEN);
1302   }
1303   else
1304   {
1305     DrawText(SX+8*32, SY+3*32, "Keyboard ", FS_BIG, FC_YELLOW);
1306     DrawText(SX+32, SY+4*32, "Customize", FS_BIG, FC_GREEN);
1307   }
1308
1309   DrawText(SX+32, SY+5*32, "Actual Settings:", FS_BIG, FC_GREEN);
1310   DrawGraphic(1, 6, GFX_PFEIL_L);
1311   DrawGraphic(1, 7, GFX_PFEIL_R);
1312   DrawGraphic(1, 8, GFX_PFEIL_O);
1313   DrawGraphic(1, 9, GFX_PFEIL_U);
1314   DrawText(SX+2*32, SY+6*32, ":", FS_BIG, FC_BLUE);
1315   DrawText(SX+2*32, SY+7*32, ":", FS_BIG, FC_BLUE);
1316   DrawText(SX+2*32, SY+8*32, ":", FS_BIG, FC_BLUE);
1317   DrawText(SX+2*32, SY+9*32, ":", FS_BIG, FC_BLUE);
1318   DrawText(SX+32, SY+10*32, "Snap Field:", FS_BIG, FC_BLUE);
1319   DrawText(SX+32, SY+12*32, "Place Bomb:", FS_BIG, FC_BLUE);
1320
1321   for (i=0; i<6; i++)
1322   {
1323     int ypos = 6 + i + (i > 3 ? i-3 : 0);
1324
1325     DrawText(SX + 3*32, SY + ypos*32,
1326              "              ", FS_BIG, FC_YELLOW);
1327     DrawText(SX + 3*32, SY + ypos*32,
1328              (setup.input[player_nr].use_joystick ?
1329               custom[i].text :
1330               getKeyNameFromKeySym(*custom[i].keysym)),
1331              FS_BIG, FC_YELLOW);
1332   }
1333 }
1334
1335 void HandleSetupInputScreen(int mx, int my, int dx, int dy, int button)
1336 {
1337   static int choice = 3;
1338   static int player_nr = 0;
1339   static int redraw = TRUE;
1340   int x = (mx+32-SX)/32, y = (my+32-SY)/32;
1341   int pos_start  = SETUPINPUT_SCREEN_POS_START  + 1;
1342   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1 + 1;
1343   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2 + 1;
1344   int pos_end    = SETUPINPUT_SCREEN_POS_END    + 1;
1345
1346   if (button == MB_MENU_INITIALIZE)
1347   {
1348     drawPlayerSetupInputInfo(player_nr);
1349     redraw = TRUE;
1350   }
1351
1352   if (redraw)
1353   {
1354     DrawGraphic(0,choice-1,GFX_KUGEL_ROT);
1355     redraw = FALSE;
1356   }
1357
1358   if (button == MB_MENU_INITIALIZE)
1359     return;
1360
1361   if (dx || dy)
1362   {
1363     if (dx && choice == 3)
1364     {
1365       x = (dx < 0 ? 11 : 13);
1366       y = 3;
1367     }
1368     else if (dx && choice == 4)
1369     {
1370       button = MB_MENU_CHOICE;
1371       x = 1;
1372       y = 4;
1373     }
1374     else if (dy)
1375     {
1376       x = 1;
1377       y = choice + dy;
1378     }
1379     else
1380       x = y = 0;
1381
1382     if (y >= pos_empty1 && y <= pos_empty2)
1383       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
1384
1385     if (y < pos_start)
1386       y = pos_start;
1387     else if (y > pos_end)
1388       y = pos_end;
1389   }
1390
1391   if (!mx && !my && !dx && !dy)
1392   {
1393     x = 1;
1394     y = choice;
1395   }
1396
1397   if (y == 3 && ((x == 1 && !button) || ((x == 11 || x == 13) && button)))
1398   {
1399     static long delay = 0;
1400
1401     if (!DelayReached(&delay, 150))
1402       goto out;
1403
1404     player_nr = (player_nr + (x == 11 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
1405
1406     drawPlayerSetupInputInfo(player_nr);
1407   }
1408   else if (x==1 && y >= pos_start && y <= pos_end &&
1409            !(y >= pos_empty1 && y <= pos_empty2))
1410   {
1411     if (button)
1412     {
1413       if (y != choice)
1414       {
1415         DrawGraphic(0, y-1, GFX_KUGEL_ROT);
1416         DrawGraphic(0, choice-1, GFX_KUGEL_BLAU);
1417       }
1418       choice = y;
1419     }
1420     else
1421     {
1422       if (y == 4)
1423       {
1424         char *device_name = setup.input[player_nr].joy.device_name;
1425
1426         if (!setup.input[player_nr].use_joystick)
1427         {
1428           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
1429
1430           setJoystickDeviceToNr(device_name, new_device_nr);
1431           setup.input[player_nr].use_joystick = TRUE;
1432         }
1433         else
1434         {
1435           int device_nr = getJoystickNrFromDeviceName(device_name);
1436           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
1437
1438           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
1439             setup.input[player_nr].use_joystick = FALSE;
1440           else
1441             setJoystickDeviceToNr(device_name, new_device_nr);
1442         }
1443
1444
1445         /*
1446         InitJoysticks();
1447         */
1448
1449
1450 #if 0
1451         int one_joystick_nr       = (dx >= 0 ? 0 : 1);
1452         int the_other_joystick_nr = (dx >= 0 ? 1 : 0);
1453
1454         if (setup.input[player_nr].use_joystick)
1455         {
1456           if (setup.input[player_nr].joystick_nr == one_joystick_nr)
1457             setup.input[player_nr].joystick_nr = the_other_joystick_nr;
1458           else
1459             setup.input[player_nr].use_joystick = FALSE;
1460         }
1461         else
1462         {
1463           setup.input[player_nr].use_joystick = TRUE;
1464           setup.input[player_nr].joystick_nr = one_joystick_nr;
1465         }
1466 #endif
1467
1468         drawPlayerSetupInputInfo(player_nr);
1469       }
1470       else if (y == 5)
1471       {
1472         if (setup.input[player_nr].use_joystick)
1473         {
1474           InitJoysticks();
1475           CalibrateJoystick(player_nr);
1476         }
1477         else
1478           CustomizeKeyboard(player_nr);
1479
1480         redraw = TRUE;
1481       }
1482       else if (y == pos_end)
1483       {
1484         InitJoysticks();
1485
1486         game_status = SETUP;
1487         DrawSetupScreen();
1488         redraw = TRUE;
1489       }
1490     }
1491   }
1492   BackToFront();
1493
1494   out:
1495
1496   if (game_status == SETUPINPUT)
1497     DoAnimation();
1498 }
1499
1500 void CustomizeKeyboard(int player_nr)
1501 {
1502   int i;
1503   int step_nr;
1504   boolean finished = FALSE;
1505   static struct SetupKeyboardInfo custom_key;
1506   static struct
1507   {
1508     KeySym *keysym;
1509     char *text;
1510   } customize_step[] =
1511   {
1512     { &custom_key.left,  "Move Left"  },
1513     { &custom_key.right, "Move Right" },
1514     { &custom_key.up,    "Move Up"    },
1515     { &custom_key.down,  "Move Down"  },
1516     { &custom_key.snap,  "Snap Field" },
1517     { &custom_key.bomb,  "Place Bomb" }
1518   };
1519
1520   /* read existing key bindings from player setup */
1521   custom_key = setup.input[player_nr].key;
1522
1523   ClearWindow();
1524   DrawText(SX + 16, SY + 16, "Keyboard Input", FS_BIG, FC_YELLOW);
1525
1526   BackToFront();
1527   InitAnimation();
1528
1529   step_nr = 0;
1530   DrawText(SX, SY + (2+2*step_nr)*32,
1531            customize_step[step_nr].text, FS_BIG, FC_RED);
1532   DrawText(SX, SY + (2+2*step_nr+1)*32,
1533            "Key:", FS_BIG, FC_RED);
1534   DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
1535            getKeyNameFromKeySym(*customize_step[step_nr].keysym),
1536            FS_BIG, FC_BLUE);
1537
1538   while(!finished)
1539   {
1540     if (XPending(display))      /* got event from X server */
1541     {
1542       XEvent event;
1543
1544       XNextEvent(display, &event);
1545
1546       switch(event.type)
1547       {
1548         case KeyPress:
1549           {
1550             KeySym key = XLookupKeysym((XKeyEvent *)&event,
1551                                        ((XKeyEvent *)&event)->state);
1552
1553             if (key == XK_Escape || (key == XK_Return && step_nr == 6))
1554             {
1555               finished = TRUE;
1556               break;
1557             }
1558
1559             /* press 'Enter' to keep the existing key binding */
1560             if (key == XK_Return || step_nr == 6)
1561               key = *customize_step[step_nr].keysym;
1562
1563             /* check if key already used */
1564             for (i=0; i<step_nr; i++)
1565               if (*customize_step[i].keysym == key)
1566                 break;
1567             if (i < step_nr)
1568               break;
1569
1570             /* got new key binding */
1571             *customize_step[step_nr].keysym = key;
1572             DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
1573                      "             ", FS_BIG, FC_YELLOW);
1574             DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
1575                      getKeyNameFromKeySym(key), FS_BIG, FC_YELLOW);
1576             step_nr++;
1577
1578             /* un-highlight last query */
1579             DrawText(SX, SY+(2+2*(step_nr-1))*32,
1580                      customize_step[step_nr-1].text, FS_BIG, FC_GREEN);
1581             DrawText(SX, SY+(2+2*(step_nr-1)+1)*32,
1582                      "Key:", FS_BIG, FC_GREEN);
1583
1584             /* press 'Enter' to leave */
1585             if (step_nr == 6)
1586             {
1587               DrawText(SX + 16, SY + 15*32+16,
1588                        "Press Enter", FS_BIG, FC_YELLOW);
1589               break;
1590             }
1591
1592             /* query next key binding */
1593             DrawText(SX, SY+(2+2*step_nr)*32,
1594                      customize_step[step_nr].text, FS_BIG, FC_RED);
1595             DrawText(SX, SY+(2+2*step_nr+1)*32,
1596                      "Key:", FS_BIG, FC_RED);
1597             DrawText(SX + 4*32, SY+(2+2*step_nr+1)*32,
1598                      getKeyNameFromKeySym(*customize_step[step_nr].keysym),
1599                      FS_BIG, FC_BLUE);
1600           }
1601           break;
1602
1603         case KeyRelease:
1604           key_joystick_mapping = 0;
1605           break;
1606
1607         default:
1608           HandleOtherEvents(&event);
1609           break;
1610       }
1611     }
1612
1613     BackToFront();
1614     DoAnimation();
1615
1616     /* don't eat all CPU time */
1617     Delay(10);
1618   }
1619
1620   /* write new key bindings back to player setup */
1621   setup.input[player_nr].key = custom_key;
1622
1623   StopAnimation();
1624   DrawSetupInputScreen();
1625 }
1626
1627 void CalibrateJoystick(int player_nr)
1628 {
1629 #ifdef __FreeBSD__
1630   struct joystick joy_ctrl;
1631 #else
1632   struct joystick_control
1633   {
1634     int buttons;
1635     int x;
1636     int y;
1637   } joy_ctrl;
1638 #endif
1639
1640 #ifdef MSDOS
1641   char joy_nr[4];
1642 #endif
1643
1644   int joystick_fd = stored_player[player_nr].joystick_fd;
1645   int new_joystick_xleft = 128, new_joystick_xright = 128;
1646   int new_joystick_yupper = 128, new_joystick_ylower = 128;
1647   int new_joystick_xmiddle, new_joystick_ymiddle;
1648   int x, y, last_x, last_y, xpos = 8, ypos = 3;
1649   boolean check[3][3];
1650   int check_remaining = 3 * 3;
1651   int joy;
1652   int result = -1;
1653
1654   if (joystick_status == JOYSTICK_OFF)
1655     return;
1656
1657   if (!setup.input[player_nr].use_joystick || joystick_fd < 0)
1658     return;
1659
1660   ClearWindow();
1661   DrawText(SX,      SY +  6*32, " ROTATE JOYSTICK ",FS_BIG,FC_YELLOW);
1662   DrawText(SX,      SY +  7*32, "IN ALL DIRECTIONS",FS_BIG,FC_YELLOW);
1663   DrawText(SX + 16, SY +  9*32, "  IF ALL BALLS  ",FS_BIG,FC_YELLOW);
1664   DrawText(SX,      SY + 10*32, "   ARE YELLOW,   ",FS_BIG,FC_YELLOW);
1665   DrawText(SX,      SY + 11*32, "  PRESS BUTTON!  ",FS_BIG,FC_YELLOW);
1666
1667   for(y=0; y<3; y++)
1668   {
1669     for(x=0; x<3; x++)
1670     {
1671       check[x][y] = FALSE;
1672       DrawGraphic(xpos + x - 1, ypos + y - 1, GFX_KUGEL_BLAU);
1673     }
1674   }
1675
1676   joy = Joystick(player_nr);
1677   last_x = (joy & JOY_LEFT ? -1 : joy & JOY_RIGHT ? +1 : 0);
1678   last_y = (joy & JOY_UP   ? -1 : joy & JOY_DOWN  ? +1 : 0);
1679   DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_ROT);
1680
1681   BackToFront();
1682
1683 #ifdef __FreeBSD__
1684   joy_ctrl.b1 = joy_ctrl.b2 = 0;
1685 #else
1686   joy_ctrl.buttons = 0;
1687 #endif
1688
1689   while(Joystick(player_nr) & JOY_BUTTON);
1690
1691   InitAnimation();
1692
1693   while(result < 0)
1694   {
1695     if (XPending(display))      /* got event from X server */
1696     {
1697       XEvent event;
1698
1699       XNextEvent(display, &event);
1700
1701       switch(event.type)
1702       {
1703         case KeyPress:
1704           switch(XLookupKeysym((XKeyEvent *)&event,
1705                                ((XKeyEvent *)&event)->state))
1706           {
1707             case XK_Return:
1708               if (check_remaining == 0)
1709                 result = 1;
1710               break;
1711
1712             case XK_Escape:
1713               result = 0;
1714               break;
1715
1716             default:
1717               break;
1718           }
1719           break;
1720
1721         case KeyRelease:
1722           key_joystick_mapping = 0;
1723           break;
1724
1725         default:
1726           HandleOtherEvents(&event);
1727           break;
1728       }
1729     }
1730
1731     if (read(joystick_fd, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
1732     {
1733       joystick_status = JOYSTICK_OFF;
1734       goto error_out;
1735     }
1736
1737     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_ctrl.x);
1738     new_joystick_xright = MAX(new_joystick_xright, joy_ctrl.x);
1739     new_joystick_yupper = MIN(new_joystick_yupper, joy_ctrl.y);
1740     new_joystick_ylower = MAX(new_joystick_ylower, joy_ctrl.y);
1741
1742     new_joystick_xmiddle =
1743       new_joystick_xleft + (new_joystick_xright - new_joystick_xleft) / 2;
1744     new_joystick_ymiddle =
1745       new_joystick_yupper + (new_joystick_ylower - new_joystick_yupper) / 2;
1746
1747     setup.input[player_nr].joy.xleft = new_joystick_xleft;
1748     setup.input[player_nr].joy.yupper = new_joystick_yupper;
1749     setup.input[player_nr].joy.xright = new_joystick_xright;
1750     setup.input[player_nr].joy.ylower = new_joystick_ylower;
1751     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
1752     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
1753
1754     CheckJoystickData();
1755
1756     joy = Joystick(player_nr);
1757
1758     if (joy & JOY_BUTTON && check_remaining == 0)
1759       result = 1;
1760
1761     x = (joy & JOY_LEFT ? -1 : joy & JOY_RIGHT ? +1 : 0);
1762     y = (joy & JOY_UP   ? -1 : joy & JOY_DOWN  ? +1 : 0);
1763
1764     if (x != last_x || y != last_y)
1765     {
1766       DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_GELB);
1767       DrawGraphic(xpos + x,      ypos + y,      GFX_KUGEL_ROT);
1768
1769       last_x = x;
1770       last_y = y;
1771
1772       if (check_remaining > 0 && !check[x+1][y+1])
1773       {
1774         check[x+1][y+1] = TRUE;
1775         check_remaining--;
1776       }
1777
1778 #if 0
1779       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
1780              setup.input[player_nr].joy.xleft,
1781              setup.input[player_nr].joy.xmiddle,
1782              setup.input[player_nr].joy.xright);
1783       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
1784              setup.input[player_nr].joy.yupper,
1785              setup.input[player_nr].joy.ymiddle,
1786              setup.input[player_nr].joy.ylower);
1787 #endif
1788     }
1789
1790     BackToFront();
1791     DoAnimation();
1792
1793     /* don't eat all CPU time */
1794     Delay(10);
1795   }
1796
1797   StopAnimation();
1798
1799   DrawSetupInputScreen();
1800   while(Joystick(player_nr) & JOY_BUTTON);
1801   return;
1802
1803   error_out:
1804
1805   ClearWindow();
1806   DrawText(SX+16, SY+16, "NO JOYSTICK",FS_BIG,FC_YELLOW);
1807   DrawText(SX+16, SY+48, " AVAILABLE ",FS_BIG,FC_YELLOW);
1808   BackToFront();
1809   Delay(3000);
1810   DrawSetupInputScreen();
1811 }
1812
1813
1814
1815 #if 0
1816
1817 void CalibrateJoystick_OLD()
1818 {
1819 #ifdef __FreeBSD__
1820   struct joystick joy_ctrl;
1821 #else
1822   struct joystick_control
1823   {
1824     int buttons;
1825     int x;
1826     int y;
1827   } joy_ctrl;
1828 #endif
1829
1830 #ifdef MSDOS
1831   char joy_nr[4];
1832 #endif
1833
1834   int joystick_nr = setup.input[0].joystick_nr;
1835   int new_joystick_xleft, new_joystick_xright, new_joystick_xmiddle;
1836   int new_joystick_yupper, new_joystick_ylower, new_joystick_ymiddle;
1837
1838   if (joystick_status == JOYSTICK_OFF)
1839     goto error_out;
1840
1841 #ifndef MSDOS
1842   ClearWindow();
1843   DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
1844   DrawText(SX+16, SY+8*32, " THE UPPER LEFT ",FS_BIG,FC_YELLOW);
1845   DrawText(SX+16, SY+9*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
1846   BackToFront();
1847
1848 #ifdef __FreeBSD__
1849   joy_ctrl.b1 = joy_ctrl.b2 = 0;
1850 #else
1851   joy_ctrl.buttons = 0;
1852 #endif
1853   while(Joystick() & JOY_BUTTON);
1854 #ifdef __FreeBSD__
1855   while(!(joy_ctrl.b1 || joy_ctrl.b2))
1856 #else
1857   while(!joy_ctrl.buttons)
1858 #endif
1859   {
1860     if (read(joystick_device, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
1861     {
1862       joystick_status=JOYSTICK_OFF;
1863       goto error_out;
1864     }
1865     Delay(10);
1866   }
1867
1868   new_joystick_xleft = joy_ctrl.x;
1869   new_joystick_yupper = joy_ctrl.y;
1870
1871   ClearWindow();
1872   DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
1873   DrawText(SX+32, SY+8*32, "THE LOWER RIGHT",FS_BIG,FC_YELLOW);
1874   DrawText(SX+16, SY+9*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
1875   BackToFront();
1876
1877 #ifdef __FreeBSD__
1878   joy_ctrl.b1 = joy_ctrl.b2 = 0;
1879 #else
1880   joy_ctrl.buttons = 0;
1881 #endif
1882   while(Joystick() & JOY_BUTTON);
1883 #ifdef __FreeBSD__
1884   while(!(joy_ctrl.b1 || joy_ctrl.b2))
1885 #else
1886   while(!joy_ctrl.buttons)
1887 #endif
1888   {
1889     if (read(joystick_device, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
1890     {
1891       joystick_status=JOYSTICK_OFF;
1892       goto error_out;
1893     }
1894     Delay(10);
1895   }
1896
1897   new_joystick_xright = joy_ctrl.x;
1898   new_joystick_ylower = joy_ctrl.y;
1899
1900   ClearWindow();
1901   DrawText(SX+32, SY+16+7*32, "CENTER JOYSTICK",FS_BIG,FC_YELLOW);
1902   DrawText(SX+16, SY+16+8*32, "AND PRESS BUTTON",FS_BIG,FC_YELLOW);
1903   BackToFront();
1904
1905 #ifdef __FreeBSD__
1906   joy_ctrl.b1 = joy_ctrl.b2 = 0;
1907 #else
1908   joy_ctrl.buttons = 0;
1909 #endif
1910   while(Joystick() & JOY_BUTTON);
1911 #ifdef __FreeBSD__
1912   while(!(joy_ctrl.b1 || joy_ctrl.b2))
1913 #else
1914   while(!joy_ctrl.buttons)
1915 #endif
1916   {
1917     if (read(joystick_device, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
1918     {
1919       joystick_status=JOYSTICK_OFF;
1920       goto error_out;
1921     }
1922     Delay(10);
1923   }
1924
1925   new_joystick_xmiddle = joy_ctrl.x;
1926   new_joystick_ymiddle = joy_ctrl.y;
1927
1928   setup.input[player_nr].joy.xleft = new_joystick_xleft;
1929   setup.input[player_nr].joy.yupper = new_joystick_yupper;
1930   setup.input[player_nr].joy.xright = new_joystick_xright;
1931   setup.input[player_nr].joy.ylower = new_joystick_ylower;
1932   setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
1933   setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
1934
1935   CheckJoystickData();
1936
1937   DrawSetupScreen();
1938   while(Joystick() & JOY_BUTTON);
1939   return;
1940
1941 #endif
1942   error_out:
1943
1944 #ifdef MSDOS
1945   joy_nr[0] = '#';
1946   joy_nr[1] = SETUP_2ND_JOYSTICK_ON(local_player->setup)+49;
1947   joy_nr[2] = '\0';
1948
1949   remove_joystick();
1950   ClearWindow();
1951   DrawText(SX+32, SY+7*32, "CENTER JOYSTICK",FS_BIG,FC_YELLOW);
1952   DrawText(SX+16+7*32, SY+8*32, joy_nr, FS_BIG,FC_YELLOW);
1953   DrawText(SX+32, SY+9*32, "AND PRESS A KEY",FS_BIG,FC_YELLOW);
1954   BackToFront();
1955
1956   for(clear_keybuf();!keypressed(););
1957   install_joystick(JOY_TYPE_2PADS);
1958
1959   ClearWindow();
1960   DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
1961   DrawText(SX+16, SY+8*32, " THE UPPER LEFT ",FS_BIG,FC_YELLOW);
1962   DrawText(SX+32, SY+9*32, "AND PRESS A KEY",FS_BIG,FC_YELLOW);
1963   BackToFront();
1964
1965   for(clear_keybuf();!keypressed(););
1966   calibrate_joystick(SETUP_2ND_JOYSTICK_ON(local_player->setup));
1967
1968   ClearWindow();
1969   DrawText(SX+16, SY+7*32, "MOVE JOYSTICK TO",FS_BIG,FC_YELLOW);
1970   DrawText(SX+32, SY+8*32, "THE LOWER RIGHT",FS_BIG,FC_YELLOW);
1971   DrawText(SX+32, SY+9*32, "AND PRESS A KEY",FS_BIG,FC_YELLOW);
1972   BackToFront();
1973
1974   for(clear_keybuf();!keypressed(););
1975   calibrate_joystick(SETUP_2ND_JOYSTICK_ON(local_player->setup));
1976
1977   DrawSetupScreen();
1978   return;
1979 #endif
1980
1981   ClearWindow();
1982   DrawText(SX+16, SY+16, "NO JOYSTICK",FS_BIG,FC_YELLOW);
1983   DrawText(SX+16, SY+48, " AVAILABLE ",FS_BIG,FC_YELLOW);
1984   BackToFront();
1985   Delay(3000);
1986   DrawSetupScreen();
1987 }
1988
1989 #endif
1990
1991
1992
1993 void HandleGameActions()
1994 {
1995   if (game_status != PLAYING)
1996     return;
1997
1998   if (local_player->LevelSolved)
1999     GameWon();
2000
2001   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
2002     TapeStop();
2003
2004   GameActions();
2005
2006   BackToFront();
2007 }
2008
2009 void HandleVideoButtons(int mx, int my, int button)
2010 {
2011   if (game_status != MAINMENU && game_status != PLAYING)
2012     return;
2013
2014   switch(CheckVideoButtons(mx,my,button))
2015   {
2016     case BUTTON_VIDEO_EJECT:
2017       TapeStop();
2018       if (TAPE_IS_EMPTY(tape))
2019       {
2020         LoadTape(level_nr);
2021         if (TAPE_IS_EMPTY(tape))
2022           Request("No tape for this level !",REQ_CONFIRM);
2023       }
2024       else
2025       {
2026         if (tape.changed)
2027           SaveTape(tape.level_nr);
2028         TapeErase();
2029       }
2030       DrawCompleteVideoDisplay();
2031       break;
2032
2033     case BUTTON_VIDEO_STOP:
2034       TapeStop();
2035       break;
2036
2037     case BUTTON_VIDEO_PAUSE:
2038       TapeTogglePause();
2039       break;
2040
2041     case BUTTON_VIDEO_REC:
2042       if (TAPE_IS_STOPPED(tape))
2043       {
2044         TapeStartRecording();
2045
2046         if (options.network)
2047           SendToServer_StartPlaying();
2048         else
2049         {
2050           game_status = PLAYING;
2051           InitGame();
2052         }
2053       }
2054       else if (tape.pausing)
2055       {
2056         if (tape.playing)       /* PLAYING -> PAUSING -> RECORDING */
2057         {
2058           tape.pos[tape.counter].delay = tape.delay_played;
2059           tape.playing = FALSE;
2060           tape.recording = TRUE;
2061           tape.changed = TRUE;
2062
2063           DrawVideoDisplay(VIDEO_STATE_PLAY_OFF | VIDEO_STATE_REC_ON,0);
2064         }
2065         else
2066           TapeTogglePause();
2067       }
2068       break;
2069
2070     case BUTTON_VIDEO_PLAY:
2071       if (TAPE_IS_EMPTY(tape))
2072         break;
2073
2074       if (TAPE_IS_STOPPED(tape))
2075       {
2076         TapeStartPlaying();
2077
2078         game_status = PLAYING;
2079         InitGame();
2080       }
2081       else if (tape.playing)
2082       {
2083         if (tape.pausing)                       /* PAUSE -> PLAY */
2084           TapeTogglePause();
2085         else if (!tape.fast_forward)            /* PLAY -> FAST FORWARD PLAY */
2086         {
2087           tape.fast_forward = TRUE;
2088           DrawVideoDisplay(VIDEO_STATE_FFWD_ON, 0);
2089         }
2090         else if (!tape.pause_before_death)      /* FFWD PLAY -> + AUTO PAUSE */
2091         {
2092           tape.pause_before_death = TRUE;
2093           DrawVideoDisplay(VIDEO_STATE_PBEND_ON, VIDEO_DISPLAY_LABEL_ONLY);
2094         }
2095         else                                    /* -> NORMAL PLAY */
2096         {
2097           tape.fast_forward = FALSE;
2098           tape.pause_before_death = FALSE;
2099           DrawVideoDisplay(VIDEO_STATE_FFWD_OFF | VIDEO_STATE_PBEND_OFF, 0);
2100         }
2101       }
2102       break;
2103
2104     default:
2105       break;
2106   }
2107
2108   BackToFront();
2109 }
2110
2111 void HandleSoundButtons(int mx, int my, int button)
2112 {
2113   if (game_status != PLAYING)
2114     return;
2115
2116   switch(CheckSoundButtons(mx,my,button))
2117   {
2118     case BUTTON_SOUND_MUSIC:
2119       if (setup.sound_music)
2120       { 
2121         setup.sound_music = FALSE;
2122         FadeSound(background_loop[level_nr % num_bg_loops]);
2123         DrawSoundDisplay(BUTTON_SOUND_MUSIC_OFF);
2124       }
2125       else if (sound_loops_allowed)
2126       { 
2127         setup.sound = setup.sound_music = TRUE;
2128         PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
2129         DrawSoundDisplay(BUTTON_SOUND_MUSIC_ON);
2130       }
2131       else
2132         DrawSoundDisplay(BUTTON_SOUND_MUSIC_OFF);
2133       break;
2134
2135     case BUTTON_SOUND_LOOPS:
2136       if (setup.sound_loops)
2137       { 
2138         setup.sound_loops = FALSE;
2139         DrawSoundDisplay(BUTTON_SOUND_LOOPS_OFF);
2140       }
2141       else if (sound_loops_allowed)
2142       { 
2143         setup.sound = setup.sound_loops = TRUE;
2144         DrawSoundDisplay(BUTTON_SOUND_LOOPS_ON);
2145       }
2146       else
2147         DrawSoundDisplay(BUTTON_SOUND_LOOPS_OFF);
2148       break;
2149
2150     case BUTTON_SOUND_SIMPLE:
2151       if (setup.sound_simple)
2152       { 
2153         setup.sound_simple = FALSE;
2154         DrawSoundDisplay(BUTTON_SOUND_SIMPLE_OFF);
2155       }
2156       else if (sound_status==SOUND_AVAILABLE)
2157       { 
2158         setup.sound = setup.sound_simple = TRUE;
2159         DrawSoundDisplay(BUTTON_SOUND_SIMPLE_ON);
2160       }
2161       else
2162         DrawSoundDisplay(BUTTON_SOUND_SIMPLE_OFF);
2163       break;
2164
2165     default:
2166       break;
2167   }
2168
2169   BackToFront();
2170 }
2171
2172 void HandleGameButtons(int mx, int my, int button)
2173 {
2174   if (game_status != PLAYING)
2175     return;
2176
2177   switch(CheckGameButtons(mx,my,button))
2178   {
2179     case BUTTON_GAME_STOP:
2180       if (AllPlayersGone)
2181       {
2182         CloseDoor(DOOR_CLOSE_1);
2183         game_status = MAINMENU;
2184         DrawMainMenu();
2185         break;
2186       }
2187
2188       if (Request("Do you really want to quit the game ?",
2189                   REQ_ASK | REQ_STAY_CLOSED))
2190       { 
2191         if (options.network)
2192           SendToServer_StopPlaying();
2193         else
2194         {
2195           game_status = MAINMENU;
2196           DrawMainMenu();
2197         }
2198       }
2199       else
2200         OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
2201       break;
2202
2203     case BUTTON_GAME_PAUSE:
2204       if (options.network)
2205       {
2206         if (tape.pausing)
2207           SendToServer_ContinuePlaying();
2208         else
2209           SendToServer_PausePlaying();
2210       }
2211       else
2212         TapeTogglePause();
2213       break;
2214
2215     case BUTTON_GAME_PLAY:
2216       if (tape.pausing)
2217       {
2218         if (options.network)
2219           SendToServer_ContinuePlaying();
2220         else
2221         {
2222           tape.pausing = FALSE;
2223           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
2224         }
2225       }
2226       break;
2227
2228     default:
2229       break;
2230   }
2231
2232   BackToFront();
2233 }