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