rnd-20020330-1-src
[rocksndiamonds.git] / src / screens.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2001 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * screens.c                                                *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "screens.h"
17 #include "events.h"
18 #include "game.h"
19 #include "tools.h"
20 #include "editor.h"
21 #include "files.h"
22 #include "tape.h"
23 #include "cartoons.h"
24 #include "network.h"
25 #include "init.h"
26
27 /* for DrawSetupScreen(), HandleSetupScreen() */
28 #define SETUP_SCREEN_POS_START          0
29 #define SETUP_SCREEN_POS_END            (SCR_FIELDY - 3)
30 #define SETUP_SCREEN_POS_EMPTY1         (SETUP_SCREEN_POS_END - 2)
31 #define SETUP_SCREEN_POS_EMPTY2         (SETUP_SCREEN_POS_END - 2)
32
33 /* for HandleSetupInputScreen() */
34 #define SETUPINPUT_SCREEN_POS_START     0
35 #define SETUPINPUT_SCREEN_POS_END       (SCR_FIELDY - 4)
36 #define SETUPINPUT_SCREEN_POS_EMPTY1    (SETUPINPUT_SCREEN_POS_START + 3)
37 #define SETUPINPUT_SCREEN_POS_EMPTY2    (SETUPINPUT_SCREEN_POS_END - 1)
38
39 /* for HandleChooseLevel() */
40 #define MAX_MENU_ENTRIES_ON_SCREEN      (SCR_FIELDY - 2)
41 #define MENU_SCREEN_START_YPOS          2
42 #define MENU_SCREEN_VALUE_XPOS          14
43
44 /* buttons and scrollbars identifiers */
45 #define SCREEN_CTRL_ID_SCROLL_UP        0
46 #define SCREEN_CTRL_ID_SCROLL_DOWN      1
47 #define SCREEN_CTRL_ID_SCROLL_VERTICAL  2
48
49 #define NUM_SCREEN_SCROLLBUTTONS        2
50 #define NUM_SCREEN_SCROLLBARS           1
51 #define NUM_SCREEN_GADGETS              3
52
53 /* forward declaration for internal use */
54 static void HandleScreenGadgets(struct GadgetInfo *);
55
56 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
57
58 void DrawHeadline()
59 {
60   int x = SX + (SXSIZE - strlen(PROGRAM_TITLE_STRING) * FONT1_XSIZE) / 2;
61
62   DrawText(x, SY + 8, PROGRAM_TITLE_STRING, FS_BIG, FC_YELLOW);
63   DrawTextFCentered(46, FC_RED, WINDOW_SUBTITLE_STRING);
64 }
65
66 static void ToggleFullscreenIfNeeded()
67 {
68   if (setup.fullscreen != video.fullscreen_enabled)
69   {
70     /* save old door content */
71     BlitBitmap(backbuffer, pix[PIX_DB_DOOR],
72                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
73
74     /* toggle fullscreen */
75     ChangeVideoModeIfNeeded(setup.fullscreen);
76     setup.fullscreen = video.fullscreen_enabled;
77
78     /* redraw background to newly created backbuffer */
79     BlitBitmap(pix[PIX_BACK], backbuffer, 0,0, WIN_XSIZE,WIN_YSIZE, 0,0);
80
81     /* restore old door content */
82     BlitBitmap(pix[PIX_DB_DOOR], backbuffer,
83                DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
84
85     redraw_mask = REDRAW_ALL;
86   }
87 }
88
89 void DrawMainMenu()
90 {
91   static struct LevelDirInfo *leveldir_last_valid = NULL;
92   int i;
93   char *name_text = (!options.network && setup.team_mode ? "Team:" : "Name:");
94
95   UnmapAllGadgets();
96   FadeSounds();
97   KeyboardAutoRepeatOn();
98   ActivateJoystickIfAvailable();
99
100   /* needed if last screen was the playing screen, invoked from level editor */
101   if (level_editor_test_game)
102   {
103     game_status = LEVELED;
104     DrawLevelEd();
105     return;
106   }
107
108   /* needed if last screen was the editor screen */
109   UndrawSpecialEditorDoor();
110
111   /* needed if last screen was the setup screen and fullscreen state changed */
112   ToggleFullscreenIfNeeded();
113
114 #ifdef TARGET_SDL
115   SetDrawtoField(DRAW_BACKBUFFER);
116 #endif
117
118   /* map gadgets for main menu screen */
119   MapTapeButtons();
120
121   /* leveldir_current may be invalid (level group, parent link) */
122   if (!validLevelSeries(leveldir_current))
123     leveldir_current = getFirstValidLevelSeries(leveldir_last_valid);
124
125   /* store valid level series information */
126   leveldir_last_valid = leveldir_current;
127
128   /* level_nr may have been set to value over handicap with level editor */
129   if (setup.handicap && level_nr > leveldir_current->handicap_level)
130     level_nr = leveldir_current->handicap_level;
131
132   GetPlayerConfig();
133   LoadLevel(level_nr);
134
135   ClearWindow();
136   DrawHeadline();
137   DrawText(SX + 32,    SY + 2*32, name_text, FS_BIG, FC_GREEN);
138   DrawText(SX + 6*32,  SY + 2*32, setup.player_name, FS_BIG, FC_RED);
139   DrawText(SX + 32,    SY + 3*32, "Level:", FS_BIG, FC_GREEN);
140   DrawText(SX + 11*32, SY + 3*32, int2str(level_nr,3), FS_BIG,
141            (leveldir_current->readonly ? FC_RED : FC_YELLOW));
142   DrawText(SX + 32,    SY + 4*32, "Hall Of Fame", FS_BIG, FC_GREEN);
143   DrawText(SX + 32,    SY + 5*32, "Level Creator", FS_BIG, FC_GREEN);
144   DrawText(SY + 32,    SY + 6*32, "Info Screen", FS_BIG, FC_GREEN);
145   DrawText(SX + 32,    SY + 7*32, "Start Game", FS_BIG, FC_GREEN);
146   DrawText(SX + 32,    SY + 8*32, "Setup", FS_BIG, FC_GREEN);
147   DrawText(SX + 32,    SY + 9*32, "Quit", FS_BIG, FC_GREEN);
148
149   DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, TRUE);
150
151   DrawTextF(7*32 + 6, 3*32 + 9, FC_RED, "%d-%d",
152             leveldir_current->first_level,
153             leveldir_current->last_level);
154
155   if (leveldir_current->readonly)
156   {
157     DrawTextF(15*32 + 6, 3*32 + 9 - 7, FC_RED, "READ");
158     DrawTextF(15*32 + 6, 3*32 + 9 + 7, FC_RED, "ONLY");
159   }
160
161   for(i=2; i<10; i++)
162     DrawGraphic(0, i, GFX_KUGEL_BLAU);
163   DrawGraphic(10, 3, GFX_ARROW_BLUE_LEFT);
164   DrawGraphic(14, 3, GFX_ARROW_BLUE_RIGHT);
165
166   DrawText(SX + 56, SY + 326, "A Game by Artsoft Entertainment",
167            FS_SMALL, FC_RED);
168
169   if (leveldir_current->name)
170   {
171     int len = strlen(leveldir_current->name);
172     int lxpos = SX + (SXSIZE - len * FONT4_XSIZE) / 2;
173     int lypos = SY + 352;
174
175     DrawText(lxpos, lypos, leveldir_current->name, FS_SMALL, FC_SPECIAL2);
176   }
177
178   FadeToFront();
179   InitAnimation();
180   HandleMainMenu(0,0, 0,0, MB_MENU_INITIALIZE);
181
182   TapeStop();
183   if (TAPE_IS_EMPTY(tape))
184     LoadTape(level_nr);
185   DrawCompleteVideoDisplay();
186
187   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
188
189 #if 0
190   ClearEventQueue();
191 #endif
192
193 }
194
195 static void gotoTopLevelDir()
196 {
197   /* move upwards to top level directory */
198   while (leveldir_current->node_parent)
199   {
200     /* write a "path" into level tree for easy navigation to last level */
201     if (leveldir_current->node_parent->node_group->cl_first == -1)
202     {
203       int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
204       int leveldir_pos = posLevelDirInfo(leveldir_current);
205       int num_page_entries;
206       int cl_first, cl_cursor;
207
208       if (num_leveldirs <= MAX_MENU_ENTRIES_ON_SCREEN)
209         num_page_entries = num_leveldirs;
210       else
211         num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1;
212
213       cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
214       cl_cursor = leveldir_pos - cl_first + 1;
215
216       leveldir_current->node_parent->node_group->cl_first = cl_first;
217       leveldir_current->node_parent->node_group->cl_cursor = cl_cursor;
218     }
219
220     leveldir_current = leveldir_current->node_parent;
221   }
222 }
223
224 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
225 {
226   static int choice = 3;
227   static int redraw = TRUE;
228   int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
229
230   if (redraw || button == MB_MENU_INITIALIZE)
231   {
232     DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
233     redraw = FALSE;
234   }
235
236   if (button == MB_MENU_INITIALIZE)
237     return;
238
239   if (dx || dy)
240   {
241     if (dx && choice == 4)
242     {
243       x = (dx < 0 ? 11 : 15);
244       y = 4;
245     }
246     else if (dy)
247     {
248       x = 1;
249       y = choice + dy;
250     }
251     else
252       x = y = 0;
253
254     if (y < 3)
255       y = 3;
256     else if (y > 10)
257       y = 10;
258   }
259
260   if (!mx && !my && !dx && !dy)
261   {
262     x = 1;
263     y = choice;
264   }
265
266   if (y == 4 && ((x == 11 && level_nr > leveldir_current->first_level) ||
267                  (x == 15 && level_nr < leveldir_current->last_level)) &&
268       button)
269   {
270     static unsigned long level_delay = 0;
271     int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
272     int new_level_nr, old_level_nr = level_nr;
273     int font_color = (leveldir_current->readonly ? FC_RED : FC_YELLOW);
274
275     new_level_nr = level_nr + (x == 11 ? -step : +step);
276     if (new_level_nr < leveldir_current->first_level)
277       new_level_nr = leveldir_current->first_level;
278     if (new_level_nr > leveldir_current->last_level)
279       new_level_nr = leveldir_current->last_level;
280
281     if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
282       new_level_nr = leveldir_current->handicap_level;
283
284     if (old_level_nr == new_level_nr ||
285         !DelayReached(&level_delay, GADGET_FRAME_DELAY))
286       goto out;
287
288     level_nr = new_level_nr;
289
290     DrawTextExt(drawto, SX + 11 * 32, SY + 3 * 32,
291                 int2str(level_nr, 3), FS_BIG, font_color);
292     DrawTextExt(window, SX + 11 * 32, SY + 3 * 32,
293                 int2str(level_nr, 3), FS_BIG, font_color);
294
295     LoadLevel(level_nr);
296     DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, TRUE);
297
298     TapeErase();
299     LoadTape(level_nr);
300     DrawCompleteVideoDisplay();
301
302     /* needed because DrawMicroLevel() takes some time */
303     BackToFront();
304     SyncDisplay();
305     DelayReached(&level_delay, 0);      /* reset delay counter */
306   }
307   else if (x == 1 && y >= 3 && y <= 10)
308   {
309     if (button)
310     {
311       if (y != choice)
312       {
313         DrawGraphic(0, y - 1, GFX_KUGEL_ROT);
314         DrawGraphic(0, choice - 1, GFX_KUGEL_BLAU);
315         choice = y;
316       }
317     }
318     else
319     {
320       if (y == 3)
321       {
322         game_status = TYPENAME;
323         HandleTypeName(strlen(setup.player_name), 0);
324       }
325       else if (y == 4)
326       {
327         if (leveldir_first)
328         {
329           game_status = CHOOSELEVEL;
330           SaveLevelSetup_LastSeries();
331           SaveLevelSetup_SeriesInfo();
332
333           gotoTopLevelDir();
334
335           DrawChooseLevel();
336         }
337       }
338       else if (y == 5)
339       {
340         game_status = HALLOFFAME;
341         DrawHallOfFame(-1);
342       }
343       else if (y == 6)
344       {
345         if (leveldir_current->readonly &&
346             strcmp(setup.player_name, "Artsoft") != 0)
347           Request("This level is read only !", REQ_CONFIRM);
348         game_status = LEVELED;
349         DrawLevelEd();
350       }
351       else if (y == 7)
352       {
353         game_status = HELPSCREEN;
354         DrawHelpScreen();
355       }
356       else if (y == 8)
357       {
358         if (setup.autorecord)
359           TapeStartRecording();
360
361 #if defined(PLATFORM_UNIX)
362         if (options.network)
363           SendToServer_StartPlaying();
364         else
365 #endif
366         {
367           game_status = PLAYING;
368           StopAnimation();
369           InitGame();
370         }
371       }
372       else if (y == 9)
373       {
374         game_status = SETUP;
375         DrawSetupScreen();
376       }
377       else if (y == 10)
378       {
379         SaveLevelSetup_LastSeries();
380         SaveLevelSetup_SeriesInfo();
381         if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
382           game_status = EXITGAME;
383       }
384
385       redraw = TRUE;
386     }
387   }
388   BackToFront();
389
390   out:
391
392   if (game_status == MAINMENU)
393   {
394     DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, FALSE);
395     DoAnimation();
396   }
397 }
398
399 #define MAX_HELPSCREEN_ELS      10
400 #define HA_NEXT                 -999
401 #define HA_END                  -1000
402
403 static long helpscreen_state;
404 static int helpscreen_step[MAX_HELPSCREEN_ELS];
405 static int helpscreen_frame[MAX_HELPSCREEN_ELS];
406 static int helpscreen_delay[MAX_HELPSCREEN_ELS];
407 static int helpscreen_action[] =
408 {
409   GFX_SPIELER1_DOWN,4,2,
410   GFX_SPIELER1_UP,4,2,
411   GFX_SPIELER1_LEFT,4,2,
412   GFX_SPIELER1_RIGHT,4,2,
413   GFX_SPIELER1_PUSH_LEFT,4,2,
414   GFX_SPIELER1_PUSH_RIGHT,4,2,                                  HA_NEXT,
415   GFX_ERDREICH,1,100,                                           HA_NEXT,
416   GFX_LEERRAUM,1,100,                                           HA_NEXT,
417   GFX_MORAST_LEER,1,100,                                        HA_NEXT,
418   GFX_BETON,1,100,                                              HA_NEXT,
419   GFX_MAUERWERK,1,100,                                          HA_NEXT,
420   GFX_MAUER_L1,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
421   GFX_MAUER_R1,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
422   GFX_MAUER_UP,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
423   GFX_MAUER_DOWN,3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,    HA_NEXT,
424   GFX_UNSICHTBAR,1,100,                                         HA_NEXT,
425   GFX_FELSBODEN,1,100,                                          HA_NEXT,
426   GFX_CHAR_A,30,4, GFX_CHAR_AUSRUF,32,4,                        HA_NEXT,
427   GFX_EDELSTEIN,2,5,                                            HA_NEXT,
428   GFX_DIAMANT,2,5,                                              HA_NEXT,
429   GFX_EDELSTEIN_BD,2,5,                                         HA_NEXT,
430   GFX_EDELSTEIN_GELB,2,5, GFX_EDELSTEIN_ROT,2,5,
431   GFX_EDELSTEIN_LILA,2,5,                                       HA_NEXT,
432   GFX_FELSBROCKEN,4,5,                                          HA_NEXT,
433   GFX_BOMBE,1,50, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10,         HA_NEXT,
434   GFX_KOKOSNUSS,1,50, GFX_CRACKINGNUT,3,1, GFX_EDELSTEIN,1,10,  HA_NEXT,
435   GFX_ERZ_EDEL,1,50, GFX_EXPLOSION,8,1, GFX_EDELSTEIN,1,10,     HA_NEXT,
436   GFX_ERZ_DIAM,1,50, GFX_EXPLOSION,8,1, GFX_DIAMANT,1,10,       HA_NEXT,
437   GFX_ERZ_EDEL_BD,1,50, GFX_EXPLOSION,8,1,GFX_EDELSTEIN_BD,1,10,HA_NEXT,
438   GFX_ERZ_EDEL_GELB,1,50, GFX_EXPLOSION,8,1,
439   GFX_EDELSTEIN_GELB,1,10, GFX_ERZ_EDEL_ROT,1,50,
440   GFX_EXPLOSION,8,1, GFX_EDELSTEIN_ROT,1,10,
441   GFX_ERZ_EDEL_LILA,1,50, GFX_EXPLOSION,8,1,
442   GFX_EDELSTEIN_LILA,1,10,                                      HA_NEXT,
443   GFX_GEBLUBBER,4,4,                                            HA_NEXT,
444   GFX_SCHLUESSEL1,4,25,                                         HA_NEXT,
445   GFX_PFORTE1,4,25,                                             HA_NEXT,
446   GFX_PFORTE1X,4,25,                                            HA_NEXT,
447   GFX_DYNAMIT_AUS,1,100,                                        HA_NEXT,
448   GFX_DYNAMIT,7,6, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10,        HA_NEXT,
449   GFX_DYNABOMB+0,4,3, GFX_DYNABOMB+3,1,3, GFX_DYNABOMB+2,1,3,
450   GFX_DYNABOMB+1,1,3, GFX_DYNABOMB+0,1,3, GFX_EXPLOSION,8,1,
451   GFX_LEERRAUM,1,10,                                            HA_NEXT,
452   GFX_DYNABOMB_NR,1,100,                                        HA_NEXT,
453   GFX_DYNABOMB_SZ,1,100,                                        HA_NEXT,
454   GFX_FLIEGER+4,1,3, GFX_FLIEGER+0,1,3, GFX_FLIEGER+4,1,3,
455   GFX_FLIEGER+5,1,3, GFX_FLIEGER+1,1,3, GFX_FLIEGER+5,1,3,
456   GFX_FLIEGER+6,1,3, GFX_FLIEGER+2,1,3, GFX_FLIEGER+6,1,3,
457   GFX_FLIEGER+7,1,3, GFX_FLIEGER+3,1,3, GFX_FLIEGER+7,1,3,      HA_NEXT,
458   GFX_KAEFER+4,1,1, GFX_KAEFER+0,1,1, GFX_KAEFER+4,1,1,
459   GFX_KAEFER+5,1,1, GFX_KAEFER+1,1,1, GFX_KAEFER+5,1,1,
460   GFX_KAEFER+6,1,1, GFX_KAEFER+2,1,1, GFX_KAEFER+6,1,1,
461   GFX_KAEFER+7,1,1, GFX_KAEFER+3,1,1, GFX_KAEFER+7,1,1,         HA_NEXT,
462   GFX_BUTTERFLY,2,2,                                            HA_NEXT,
463   GFX_FIREFLY,2,2,                                              HA_NEXT,
464   GFX_PACMAN+0,1,3, GFX_PACMAN+4,1,2, GFX_PACMAN+0,1,3,
465   GFX_PACMAN+1,1,3, GFX_PACMAN+5,1,2, GFX_PACMAN+1,1,3,
466   GFX_PACMAN+2,1,3, GFX_PACMAN+6,1,2, GFX_PACMAN+2,1,3,
467   GFX_PACMAN+3,1,3, GFX_PACMAN+7,1,2, GFX_PACMAN+3,1,3,         HA_NEXT,
468   GFX_MAMPFER+0,4,1, GFX_MAMPFER+3,1,1, GFX_MAMPFER+2,1,1,
469   GFX_MAMPFER+1,1,1, GFX_MAMPFER+0,1,1,                         HA_NEXT,
470   GFX_MAMPFER2+0,4,1, GFX_MAMPFER2+3,1,1, GFX_MAMPFER2+2,1,1,
471   GFX_MAMPFER2+1,1,1, GFX_MAMPFER2+0,1,1,                       HA_NEXT,
472   GFX_ROBOT+0,4,1, GFX_ROBOT+3,1,1, GFX_ROBOT+2,1,1,
473   GFX_ROBOT+1,1,1, GFX_ROBOT+0,1,1,                             HA_NEXT,
474   GFX_MOLE_DOWN,4,2,
475   GFX_MOLE_UP,4,2,
476   GFX_MOLE_LEFT,4,2,
477   GFX_MOLE_RIGHT,4,2,                                           HA_NEXT,
478   GFX_PINGUIN_DOWN,4,2,
479   GFX_PINGUIN_UP,4,2,
480   GFX_PINGUIN_LEFT,4,2,
481   GFX_PINGUIN_RIGHT,4,2,                                        HA_NEXT,
482   GFX_SCHWEIN_DOWN,4,2,
483   GFX_SCHWEIN_UP,4,2,
484   GFX_SCHWEIN_LEFT,4,2,
485   GFX_SCHWEIN_RIGHT,4,2,                                        HA_NEXT,
486   GFX_DRACHE_DOWN,4,2,
487   GFX_DRACHE_UP,4,2,
488   GFX_DRACHE_LEFT,4,2,
489   GFX_DRACHE_RIGHT,4,2,                                         HA_NEXT,
490   GFX_SONDE_START,8,1,                                          HA_NEXT,
491   GFX_ABLENK,4,1,                                               HA_NEXT,
492   GFX_BIRNE_AUS,1,25, GFX_BIRNE_EIN,1,25,                       HA_NEXT,
493   GFX_ZEIT_VOLL,1,25, GFX_ZEIT_LEER,1,25,                       HA_NEXT,
494   GFX_TROPFEN,1,25, GFX_AMOEBING,4,1, GFX_AMOEBE_LEBT,1,10,     HA_NEXT,
495   GFX_AMOEBE_TOT+2,2,50, GFX_AMOEBE_TOT,2,50,                   HA_NEXT,
496   GFX_AMOEBE_LEBT,4,40,                                         HA_NEXT,
497   GFX_AMOEBE_LEBT,1,10, GFX_AMOEBING,4,2,                       HA_NEXT,
498   GFX_AMOEBE_LEBT,1,25, GFX_AMOEBE_TOT,1,25, GFX_EXPLOSION,8,1,
499   GFX_DIAMANT,1,10,                                             HA_NEXT,
500   GFX_LIFE,1,100,                                               HA_NEXT,
501   GFX_LIFE_ASYNC,1,100,                                         HA_NEXT,
502   GFX_MAGIC_WALL_OFF,4,2,                                       HA_NEXT,
503   GFX_MAGIC_WALL_BD_OFF,4,2,                                    HA_NEXT,
504   GFX_AUSGANG_ZU,1,100, GFX_AUSGANG_ACT,4,2,
505   GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2,
506   GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2,                 HA_NEXT,
507   GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2,
508   GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2,                 HA_NEXT,
509   GFX_SOKOBAN_OBJEKT,1,100,                                     HA_NEXT,
510   GFX_SOKOBAN_FELD_LEER,1,100,                                  HA_NEXT,
511   GFX_SOKOBAN_FELD_VOLL,1,100,                                  HA_NEXT,
512   GFX_SPEED_PILL,1,100,                                         HA_NEXT,
513   HA_END
514 };
515 static char *helpscreen_eltext[][2] =
516 {
517  {"THE HERO:",                          "(Is _this_ guy good old Rockford?)"},
518  {"Normal sand:",                       "You can dig through it"},
519  {"Empty field:",                       "You can walk through it"},
520  {"Quicksand: You cannot pass it,",     "but rocks can fall though it"},
521  {"Massive Wall:",                      "Nothing can go through it"},
522  {"Normal Wall: You can't go through",  "it, but you can bomb it away"},
523  {"Growing Wall: Grows in several di-", "rections if there is an empty field"},
524  {"Invisible Wall: Behaves like normal","wall, but is invisible"},
525  {"Old Wall: Like normal wall, but",    "some things can fall down from it"},
526  {"Letter Wall: Looks like a letter,",  "behaves like a normal wall"},
527  {"Emerald: You must collect enough of","them to finish a level"},
528  {"Diamond: Counts as 3 emeralds, but", "can be destroyed by rocks"},
529  {"Diamond (BD style): Counts like one","emerald and behaves a bit different"},
530  {"Colorful Gems:",                     "Seem to behave like Emeralds"},
531  {"Rock: Smashes several things;",      "Can be moved by the player"},
532  {"Bomb: You can move it, but be",      "careful when dropping it"},
533  {"Nut: Throw a rock on it to open it;","Each nut contains an emerald"},
534  {"Wall with an emerald inside:",       "Bomb the wall away to get it"},
535  {"Wall with a diamond inside:",        "Bomb the wall away to get it"},
536  {"Wall with BD style diamond inside:", "Bomb the wall away to get it"},
537  {"Wall with colorful gem inside:",     "Bomb the wall away to get it"},
538  {"Acid: Things that fall in are gone", "forever (including our hero)"},
539  {"Key: Opens the door that has the",   "same color (red/yellow/green/blue)"},
540  {"Door: Can be opened by the key",     "with the same color"},
541  {"Door: You have to find out the",     "right color of the key for it"},
542  {"Dynamite: Collect it and use it to", "destroy walls or kill enemies"},
543  {"Dynamite: This one explodes after",  "a few seconds"},
544  {"Dyna Bomb: Explodes in 4 directions","with variable explosion size"},
545  {"Dyna Bomb: Increases the number of", "dyna bombs available at a time"},
546  {"Dyna Bomb: Increases the size of",   "explosion of dyna bombs"},
547  {"Spaceship: Moves at the left side",  "of walls; don't touch it!"},
548  {"Bug: Moves at the right side",       "of walls; don't touch it!"},
549  {"Butterfly: Moves at the right side", "of walls; don't touch it!"},
550  {"Firefly: Moves at the left side",    "of walls; don't touch it!"},
551  {"Pacman: Eats the amoeba and you,",   "if you're not careful"},
552  {"Cruncher: Eats diamonds and you,",   "if you're not careful"},
553  {"Cruncher (BD style):",               "Eats almost everything"},
554  {"Robot: Tries to kill the player",    ""},
555  {"The mole: Eats the amoeba and turns","empty space into normal sand"},
556  {"The penguin: Guide him to the exit,","but keep him away from monsters!"},
557  {"The Pig: Harmless, but eats all",    "gems it can get"},
558  {"The Dragon: Breathes fire,",         "especially to some monsters"},
559  {"Sonde: Follows you everywhere;",     "harmless, but may block your way"},
560  {"Magic Wheel: Touch it to get rid of","the robots for some seconds"},
561  {"Light Bulb: All of them must be",    "switched on to finish a level"},
562  {"Extra Time Orb: Adds some seconds",  "to the time available for the level"},
563  {"Amoeba Drop: Grows to an amoeba on", "the ground - don't touch it"},
564  {"Dead Amoeba: Does not grow, but",    "can still kill bugs and spaceships"},
565  {"Normal Amoeba: Grows through empty", "fields, sand and quicksand"},
566  {"Dropping Amoeba: This one makes",    "drops that grow to a new amoeba"},
567  {"Living Amoeba (BD style): Contains", "other element, when surrounded"},
568  {"Game Of Life: Behaves like the well","known 'Game Of Life' (2333 style)"},
569  {"Biomaze: A bit like the 'Game Of",   "Life', but builds crazy mazes"},
570  {"Magic Wall: Changes rocks, emeralds","and diamonds when they pass it"},
571  {"Magic Wall (BD style):",             "Changes rocks and BD style diamonds"},
572  {"Exit door: Opens if you have enough","emeralds to finish the level"},
573  {"Open exit door: Enter here to leave","the level and exit the actual game"},
574  {"Sokoban element: Object which must", "be pushed to an empty field"},
575  {"Sokoban element: Empty field where", "a Sokoban object can be placed on"},
576  {"Sokoban element: Field with object", "which can be pushed away"},
577  {"Speed pill: Lets the player run",    "twice as fast as normally"},
578 };
579 static int num_helpscreen_els = sizeof(helpscreen_eltext)/(2*sizeof(char *));
580
581 static char *helpscreen_music[][3] =
582 {
583   { "Alchemy",                  "Ian Boddy",            "Drive" },
584   { "The Chase",                "Propaganda",           "A Secret Wish" },
585   { "Network 23",               "Tangerine Dream",      "Exit" },
586   { "Czardasz",                 "Robert Pieculewicz",   "Czardasz" },
587   { "21st Century Common Man",  "Tangerine Dream",      "Tyger" },
588   { "Voyager",                  "The Alan Parsons Project","Pyramid" },
589   { "Twilight Painter",         "Tangerine Dream",      "Heartbreakers" }
590 };
591 static int num_helpscreen_music = 7;
592 static int helpscreen_musicpos;
593
594 void DrawHelpScreenElAction(int start)
595 {
596   int i = 0, j = 0;
597   int frame, graphic;
598   int xstart = SX+16, ystart = SY+64+2*32, ystep = TILEY+4;
599
600   while(helpscreen_action[j] != HA_END)
601   {
602     if (i>=start+MAX_HELPSCREEN_ELS || i>=num_helpscreen_els)
603       break;
604     else if (i<start || helpscreen_delay[i-start])
605     {
606       if (i>=start && helpscreen_delay[i-start])
607         helpscreen_delay[i-start]--;
608
609       while(helpscreen_action[j] != HA_NEXT)
610         j++;
611       j++;
612       i++;
613       continue;
614     }
615
616     j += 3*helpscreen_step[i-start];
617     graphic = helpscreen_action[j++];
618
619     if (helpscreen_frame[i-start])
620     {
621       frame = helpscreen_action[j++] - helpscreen_frame[i-start];
622       helpscreen_frame[i-start]--;
623     }
624     else
625     {
626       frame = 0;
627       helpscreen_frame[i-start] = helpscreen_action[j++]-1;
628     }
629
630     helpscreen_delay[i-start] = helpscreen_action[j++] - 1;
631
632     if (helpscreen_action[j] == HA_NEXT)
633     {
634       if (!helpscreen_frame[i-start])
635         helpscreen_step[i-start] = 0;
636     }
637     else
638     {
639       if (!helpscreen_frame[i-start])
640         helpscreen_step[i-start]++;
641       while(helpscreen_action[j] != HA_NEXT)
642         j++;
643     }
644     j++;
645
646     DrawGraphicExt(drawto, xstart, ystart+(i-start)*ystep, graphic+frame);
647     i++;
648   }
649
650   for(i=2;i<16;i++)
651   {
652     MarkTileDirty(0,i);
653     MarkTileDirty(1,i);
654   }
655 }
656
657 void DrawHelpScreenElText(int start)
658 {
659   int i;
660   int xstart = SX + 56, ystart = SY + 65 + 2 * 32, ystep = TILEY + 4;
661   int ybottom = SYSIZE - 20;
662
663   ClearWindow();
664   DrawHeadline();
665
666   DrawTextFCentered(100, FC_GREEN, "The game elements:");
667
668   for(i=start; i < start + MAX_HELPSCREEN_ELS && i < num_helpscreen_els; i++)
669   {
670     DrawText(xstart,
671              ystart + (i - start) * ystep + (*helpscreen_eltext[i][1] ? 0 : 8),
672              helpscreen_eltext[i][0], FS_SMALL, FC_YELLOW);
673     DrawText(xstart, ystart + (i - start) * ystep + 16,
674              helpscreen_eltext[i][1], FS_SMALL, FC_YELLOW);
675   }
676
677   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
678 }
679
680 void DrawHelpScreenMusicText(int num)
681 {
682   int ystart = 150, ystep = 30;
683   int ybottom = SYSIZE - 20;
684
685   FadeSounds();
686   ClearWindow();
687   DrawHeadline();
688
689   DrawTextFCentered(100, FC_GREEN, "The game background music loops:");
690
691   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
692                     "Excerpt from");
693   DrawTextFCentered(ystart + 1 * ystep, FC_RED, "\"%s\"",
694                     helpscreen_music[num][0]);
695   DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW,
696                     "by");
697   DrawTextFCentered(ystart + 3 * ystep, FC_RED,
698                     "%s", helpscreen_music[num][1]);
699   DrawTextFCentered(ystart + 4 * ystep, FC_YELLOW,
700                     "from the album");
701   DrawTextFCentered(ystart + 5 * ystep, FC_RED, "\"%s\"",
702                     helpscreen_music[num][2]);
703
704   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
705
706 #if 0
707   PlaySoundLoop(background_loop[num]);
708 #endif
709 }
710
711 void DrawHelpScreenCreditsText()
712 {
713   int ystart = 150, ystep = 30;
714   int ybottom = SYSIZE - 20;
715
716   FadeSounds();
717   ClearWindow();
718   DrawHeadline();
719
720   DrawTextFCentered(100, FC_GREEN,
721                     "Credits:");
722   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
723                     "DOS port of the game:");
724   DrawTextFCentered(ystart + 1 * ystep, FC_RED,
725                     "Guido Schulz");
726   DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW,
727                     "Additional toons:");
728   DrawTextFCentered(ystart + 3 * ystep, FC_RED,
729                     "Karl Hörnell");
730   DrawTextFCentered(ystart + 5 * ystep, FC_YELLOW,
731                     "...and many thanks to all contributors");
732   DrawTextFCentered(ystart + 6 * ystep, FC_YELLOW,
733                     "of new levels!");
734
735   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
736 }
737
738 void DrawHelpScreenContactText()
739 {
740   int ystart = 150, ystep = 30;
741   int ybottom = SYSIZE - 20;
742
743   ClearWindow();
744   DrawHeadline();
745
746   DrawTextFCentered(100, FC_GREEN, "Program information:");
747
748   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
749                     "This game is Freeware!");
750   DrawTextFCentered(ystart + 1 * ystep, FC_YELLOW,
751                     "If you like it, send e-mail to:");
752   DrawTextFCentered(ystart + 2 * ystep, FC_RED,
753                     "info@artsoft.org");
754   DrawTextFCentered(ystart + 3 * ystep, FC_YELLOW,
755                     "or SnailMail to:");
756   DrawTextFCentered(ystart + 4 * ystep + 0, FC_RED,
757                     "Holger Schemel");
758   DrawTextFCentered(ystart + 4 * ystep + 20, FC_RED,
759                     "Detmolder Strasse 189");
760   DrawTextFCentered(ystart + 4 * ystep + 40, FC_RED,
761                     "33604 Bielefeld");
762   DrawTextFCentered(ystart + 4 * ystep + 60, FC_RED,
763                     "Germany");
764
765   DrawTextFCentered(ystart + 7 * ystep, FC_YELLOW,
766                     "If you have created new levels,");
767   DrawTextFCentered(ystart + 8 * ystep, FC_YELLOW,
768                     "send them to me to include them!");
769   DrawTextFCentered(ystart + 9 * ystep, FC_YELLOW,
770                     ":-)");
771
772   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for main menu");
773 }
774
775 void DrawHelpScreen()
776 {
777   int i;
778
779   UnmapAllGadgets();
780   CloseDoor(DOOR_CLOSE_2);
781
782   for(i=0;i<MAX_HELPSCREEN_ELS;i++)
783     helpscreen_step[i] = helpscreen_frame[i] = helpscreen_delay[i] = 0;
784   helpscreen_musicpos = 0;
785   helpscreen_state = 0;
786   DrawHelpScreenElText(0);
787   DrawHelpScreenElAction(0);
788
789   FadeToFront();
790   InitAnimation();
791   PlaySoundLoop(SND_RHYTHMLOOP);
792 }
793
794 void HandleHelpScreen(int button)
795 {
796   static unsigned long hs_delay = 0;
797   int num_helpscreen_els_pages =
798     (num_helpscreen_els + MAX_HELPSCREEN_ELS-1) / MAX_HELPSCREEN_ELS;
799   int button_released = !button;
800   int i;
801
802   if (button_released)
803   {
804     if (helpscreen_state < num_helpscreen_els_pages - 1)
805     {
806       for(i=0;i<MAX_HELPSCREEN_ELS;i++)
807         helpscreen_step[i] = helpscreen_frame[i] = helpscreen_delay[i] = 0;
808       helpscreen_state++;
809       DrawHelpScreenElText(helpscreen_state*MAX_HELPSCREEN_ELS);
810       DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
811     }
812     else if (helpscreen_state <
813              num_helpscreen_els_pages + num_helpscreen_music - 1)
814     {
815       helpscreen_state++;
816       DrawHelpScreenMusicText(helpscreen_state - num_helpscreen_els_pages);
817     }
818     else if (helpscreen_state ==
819              num_helpscreen_els_pages + num_helpscreen_music - 1)
820     {
821       helpscreen_state++;
822       DrawHelpScreenCreditsText();
823     }
824     else if (helpscreen_state ==
825              num_helpscreen_els_pages + num_helpscreen_music)
826     {
827       helpscreen_state++;
828       DrawHelpScreenContactText();
829     }
830     else
831     {
832       FadeSounds();
833       DrawMainMenu();
834       game_status = MAINMENU;
835     }
836   }
837   else
838   {
839     if (DelayReached(&hs_delay,GAME_FRAME_DELAY * 2))
840     {
841       if (helpscreen_state<num_helpscreen_els_pages)
842         DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
843     }
844     DoAnimation();
845   }
846
847   BackToFront();
848 }
849
850 void HandleTypeName(int newxpos, Key key)
851 {
852   static int xpos = 0, ypos = 2;
853
854   if (newxpos)
855   {
856     xpos = newxpos;
857     DrawText(SX + 6*32, SY + ypos*32, setup.player_name, FS_BIG, FC_YELLOW);
858     DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
859     return;
860   }
861
862   if (((key >= KSYM_A && key <= KSYM_Z) ||
863        (key >= KSYM_a && key <= KSYM_z)) && 
864       xpos < MAX_PLAYER_NAME_LEN)
865   {
866     char ascii;
867
868     if (key >= KSYM_A && key <= KSYM_Z)
869       ascii = 'A' + (char)(key - KSYM_A);
870     else
871       ascii = 'a' + (char)(key - KSYM_a);
872
873     setup.player_name[xpos] = ascii;
874     setup.player_name[xpos + 1] = 0;
875     xpos++;
876     DrawTextExt(drawto, SX + 6*32, SY + ypos*32,
877                 setup.player_name, FS_BIG, FC_YELLOW);
878     DrawTextExt(window, SX + 6*32, SY + ypos*32,
879                 setup.player_name, FS_BIG, FC_YELLOW);
880     DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
881   }
882   else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
883   {
884     xpos--;
885     setup.player_name[xpos] = 0;
886     DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
887     DrawGraphic(xpos + 7, ypos, GFX_LEERRAUM);
888   }
889   else if (key == KSYM_Return && xpos > 0)
890   {
891     DrawText(SX + 6*32, SY + ypos*32, setup.player_name, FS_BIG, FC_RED);
892     DrawGraphic(xpos + 6, ypos, GFX_LEERRAUM);
893
894     SaveSetup();
895     game_status = MAINMENU;
896   }
897
898   BackToFront();
899 }
900
901 static void drawCursorExt(int pos, int color, int graphic)
902 {
903   static int cursor_array[SCR_FIELDY];
904
905   if (graphic)
906     cursor_array[pos] = graphic;
907
908   graphic = cursor_array[pos];
909
910   if (color == FC_RED)
911     graphic = (graphic == GFX_ARROW_BLUE_LEFT  ? GFX_ARROW_RED_LEFT  :
912                graphic == GFX_ARROW_BLUE_RIGHT ? GFX_ARROW_RED_RIGHT :
913                GFX_KUGEL_ROT);
914
915   DrawGraphic(0, MENU_SCREEN_START_YPOS + pos, graphic);
916 }
917
918 static void initCursor(int pos, int graphic)
919 {
920   drawCursorExt(pos, FC_BLUE, graphic);
921 }
922
923 static void drawCursor(int pos, int color)
924 {
925   drawCursorExt(pos, color, 0);
926 }
927
928 void DrawChooseLevel()
929 {
930   UnmapAllGadgets();
931   CloseDoor(DOOR_CLOSE_2);
932
933   ClearWindow();
934   HandleChooseLevel(0,0, 0,0, MB_MENU_INITIALIZE);
935   MapChooseLevelGadgets();
936
937   FadeToFront();
938   InitAnimation();
939 }
940
941 static void AdjustChooseLevelScrollbar(int id, int first_entry)
942 {
943   struct GadgetInfo *gi = screen_gadget[id];
944   int items_max, items_visible, item_position;
945
946   items_max = numLevelDirInfoInGroup(leveldir_current);
947   items_visible = MAX_MENU_ENTRIES_ON_SCREEN - 1;
948   item_position = first_entry;
949
950   if (item_position > items_max - items_visible)
951     item_position = items_max - items_visible;
952
953   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
954                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
955 }
956
957 static void drawChooseLevelList(int first_entry, int num_page_entries)
958 {
959   int i;
960   char buffer[SCR_FIELDX * 2];
961   int max_buffer_len = (SCR_FIELDX - 2) * 2;
962   int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
963
964   ClearRectangle(backbuffer, SX, SY, SXSIZE - 32, SYSIZE);
965   redraw_mask |= REDRAW_FIELD;
966
967   DrawText(SX, SY, "Level Directories", FS_BIG, FC_GREEN);
968
969   for(i=0; i<num_page_entries; i++)
970   {
971     struct LevelDirInfo *node, *node_first;
972     int leveldir_pos = first_entry + i;
973     int ypos = MENU_SCREEN_START_YPOS + i;
974
975     node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
976     node = getLevelDirInfoFromPos(node_first, leveldir_pos);
977
978     strncpy(buffer, node->name , max_buffer_len);
979     buffer[max_buffer_len] = '\0';
980
981     DrawText(SX + 32, SY + ypos * 32, buffer, FS_MEDIUM, node->color);
982
983     if (node->parent_link)
984       initCursor(i, GFX_ARROW_BLUE_LEFT);
985     else if (node->level_group)
986       initCursor(i, GFX_ARROW_BLUE_RIGHT);
987     else
988       initCursor(i, GFX_KUGEL_BLAU);
989   }
990
991   if (first_entry > 0)
992     DrawGraphic(0, 1, GFX_ARROW_BLUE_UP);
993
994   if (first_entry + num_page_entries < num_leveldirs)
995     DrawGraphic(0, MAX_MENU_ENTRIES_ON_SCREEN + 1, GFX_ARROW_BLUE_DOWN);
996 }
997
998 static void drawChooseLevelInfo(int leveldir_pos)
999 {
1000   struct LevelDirInfo *node, *node_first;
1001   int x, last_redraw_mask = redraw_mask;
1002
1003   node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
1004   node = getLevelDirInfoFromPos(node_first, leveldir_pos);
1005
1006   ClearRectangle(drawto, SX + 32, SY + 32, SXSIZE - 64, 32);
1007
1008   if (node->parent_link)
1009     DrawTextFCentered(40, FC_RED, "leave group \"%s\"", node->class_desc);
1010   else if (node->level_group)
1011     DrawTextFCentered(40, FC_RED, "enter group \"%s\"", node->class_desc);
1012   else
1013     DrawTextFCentered(40, FC_RED, "%3d levels (%s)",
1014                       node->levels, node->class_desc);
1015
1016   /* let BackToFront() redraw only what is needed */
1017   redraw_mask = last_redraw_mask | REDRAW_TILES;
1018   for (x=0; x<SCR_FIELDX; x++)
1019     MarkTileDirty(x, 1);
1020 }
1021
1022 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
1023 {
1024   static unsigned long choose_delay = 0;
1025   static int redraw = TRUE;
1026   int x = (mx + 32 - SX) / 32;
1027   int y = (my + 32 - SY) / 32 - MENU_SCREEN_START_YPOS;
1028   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
1029   int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
1030   int num_page_entries;
1031
1032   if (num_leveldirs <= MAX_MENU_ENTRIES_ON_SCREEN)
1033     num_page_entries = num_leveldirs;
1034   else
1035     num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1;
1036
1037   if (button == MB_MENU_INITIALIZE)
1038   {
1039     int leveldir_pos = posLevelDirInfo(leveldir_current);
1040
1041     if (leveldir_current->cl_first == -1)
1042     {
1043       leveldir_current->cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
1044       leveldir_current->cl_cursor =
1045         leveldir_pos - leveldir_current->cl_first + 1;
1046     }
1047
1048     if (dx == 999)      /* first entry is set by scrollbar position */
1049       leveldir_current->cl_first = dy;
1050     else
1051       AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
1052                                  leveldir_current->cl_first);
1053
1054     drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
1055     drawChooseLevelInfo(leveldir_current->cl_first +
1056                         leveldir_current->cl_cursor - 1);
1057     redraw = TRUE;
1058   }
1059
1060   if (redraw)
1061   {
1062     drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
1063     redraw = FALSE;
1064   }
1065
1066   if (button == MB_MENU_INITIALIZE)
1067     return;
1068
1069   if (dx || dy)
1070   {
1071     if (dy)
1072     {
1073       x = 1;
1074       y = leveldir_current->cl_cursor + dy;
1075     }
1076     else
1077       x = y = 0;        /* no action */
1078
1079     if (ABS(dy) == SCR_FIELDY)  /* handle KSYM_Page_Up, KSYM_Page_Down */
1080     {
1081       dy = SIGN(dy);
1082       step = num_page_entries - 1;
1083       x = 1;
1084       y = (dy < 0 ? 0 : num_page_entries + 1);
1085     }
1086   }
1087
1088   if (x == 1 && y == 0)
1089   {
1090     if (leveldir_current->cl_first > 0 &&
1091         (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY)))
1092     {
1093       leveldir_current->cl_first -= step;
1094       if (leveldir_current->cl_first < 0)
1095         leveldir_current->cl_first = 0;
1096
1097       drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
1098       drawChooseLevelInfo(leveldir_current->cl_first +
1099                           leveldir_current->cl_cursor - 1);
1100       drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
1101       AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
1102                                  leveldir_current->cl_first);
1103       return;
1104     }
1105   }
1106   else if (x == 1 && y > num_page_entries)
1107   {
1108     if (leveldir_current->cl_first + num_page_entries < num_leveldirs &&
1109         (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY)))
1110     {
1111       leveldir_current->cl_first += step;
1112       if (leveldir_current->cl_first + num_page_entries > num_leveldirs)
1113         leveldir_current->cl_first = MAX(0, num_leveldirs - num_page_entries);
1114
1115       drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
1116       drawChooseLevelInfo(leveldir_current->cl_first +
1117                           leveldir_current->cl_cursor - 1);
1118       drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
1119       AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
1120                                  leveldir_current->cl_first);
1121       return;
1122     }
1123   }
1124
1125   if (!mx && !my && !dx && !dy)
1126   {
1127     x = 1;
1128     y = leveldir_current->cl_cursor;
1129   }
1130
1131   if (dx == 1)
1132   {
1133     struct LevelDirInfo *node_first, *node_cursor;
1134     int leveldir_pos =
1135       leveldir_current->cl_first + leveldir_current->cl_cursor - 1;
1136
1137     node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
1138     node_cursor = getLevelDirInfoFromPos(node_first, leveldir_pos);
1139
1140     if (node_cursor->node_group)
1141     {
1142       node_cursor->cl_first = leveldir_current->cl_first;
1143       node_cursor->cl_cursor = leveldir_current->cl_cursor;
1144       leveldir_current = node_cursor->node_group;
1145       DrawChooseLevel();
1146     }
1147   }
1148   else if (dx == -1 && leveldir_current->node_parent)
1149   {
1150     leveldir_current = leveldir_current->node_parent;
1151     DrawChooseLevel();
1152   }
1153
1154   if (x == 1 && y > 0 && y <= num_page_entries)
1155   {
1156     if (button)
1157     {
1158       if (y != leveldir_current->cl_cursor)
1159       {
1160         drawCursor(y - 1, FC_RED);
1161         drawCursor(leveldir_current->cl_cursor - 1, FC_BLUE);
1162         drawChooseLevelInfo(leveldir_current->cl_first + y - 1);
1163         leveldir_current->cl_cursor = y;
1164       }
1165     }
1166     else
1167     {
1168       struct LevelDirInfo *node_first, *node_cursor;
1169       int leveldir_pos = leveldir_current->cl_first + y - 1;
1170
1171       node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
1172       node_cursor = getLevelDirInfoFromPos(node_first, leveldir_pos);
1173
1174       if (node_cursor->node_group)
1175       {
1176         node_cursor->cl_first = leveldir_current->cl_first;
1177         node_cursor->cl_cursor = leveldir_current->cl_cursor;
1178         leveldir_current = node_cursor->node_group;
1179
1180         DrawChooseLevel();
1181       }
1182       else if (node_cursor->parent_link)
1183       {
1184         leveldir_current = node_cursor->node_parent;
1185
1186         DrawChooseLevel();
1187       }
1188       else
1189       {
1190         node_cursor->cl_first = leveldir_current->cl_first;
1191         node_cursor->cl_cursor = leveldir_current->cl_cursor;
1192         leveldir_current = node_cursor;
1193
1194         LoadLevelSetup_SeriesInfo();
1195
1196         SaveLevelSetup_LastSeries();
1197         SaveLevelSetup_SeriesInfo();
1198         TapeErase();
1199
1200         game_status = MAINMENU;
1201         DrawMainMenu();
1202       }
1203     }
1204   }
1205
1206   BackToFront();
1207
1208   if (game_status == CHOOSELEVEL)
1209     DoAnimation();
1210 }
1211
1212 void DrawHallOfFame(int highlight_position)
1213 {
1214   UnmapAllGadgets();
1215   FadeSounds();
1216   CloseDoor(DOOR_CLOSE_2);
1217
1218   if (highlight_position < 0) 
1219     LoadScore(level_nr);
1220
1221   FadeToFront();
1222   InitAnimation();
1223   HandleHallOfFame(highlight_position,0, 0,0, MB_MENU_INITIALIZE);
1224   PlaySound(SND_HALLOFFAME);
1225 }
1226
1227 static void drawHallOfFameList(int first_entry, int highlight_position)
1228 {
1229   int i;
1230
1231   ClearWindow();
1232   DrawText(SX + 80, SY + 8, "Hall Of Fame", FS_BIG, FC_YELLOW);
1233   DrawTextFCentered(46, FC_RED, "HighScores of Level %d", level_nr);
1234
1235   for(i=0; i<MAX_MENU_ENTRIES_ON_SCREEN; i++)
1236   {
1237     int entry = first_entry + i;
1238     int color = (entry == highlight_position ? FC_RED : FC_GREEN);
1239
1240 #if 0
1241     DrawText(SX, SY + 64 + i * 32, ".................", FS_BIG, color);
1242     DrawText(SX, SY + 64 + i * 32, highscore[i].Name, FS_BIG, color);
1243     DrawText(SX + 12 * 32, SY + 64 + i * 32,
1244              int2str(highscore[i].Score, 5), FS_BIG, color);
1245 #else
1246     DrawText(SX, SY + 64 + i * 32, "..................................",
1247              FS_MEDIUM, FC_YELLOW);
1248     DrawText(SX, SY + 64 + i * 32, int2str(entry + 1, 3),
1249              FS_MEDIUM, FC_YELLOW);
1250     DrawText(SX + 64, SY + 64 + i * 32, highscore[entry].Name, FS_BIG, color);
1251     DrawText(SX + 14 * 32 + 16, SY + 64 + i * 32,
1252              int2str(highscore[entry].Score, 5), FS_MEDIUM, color);
1253 #endif
1254   }
1255 }
1256
1257 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
1258 {
1259   static int first_entry = 0;
1260   static int highlight_position = 0;
1261   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
1262   int button_released = !button;
1263
1264   if (button == MB_MENU_INITIALIZE)
1265   {
1266     first_entry = 0;
1267     highlight_position = mx;
1268     drawHallOfFameList(first_entry, highlight_position);
1269     return;
1270   }
1271
1272   if (ABS(dy) == SCR_FIELDY)    /* handle KSYM_Page_Up, KSYM_Page_Down */
1273     step = MAX_MENU_ENTRIES_ON_SCREEN - 1;
1274
1275   if (dy < 0)
1276   {
1277     if (first_entry > 0)
1278     {
1279       first_entry -= step;
1280       if (first_entry < 0)
1281         first_entry = 0;
1282
1283       drawHallOfFameList(first_entry, highlight_position);
1284       return;
1285     }
1286   }
1287   else if (dy > 0)
1288   {
1289     if (first_entry + MAX_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
1290     {
1291       first_entry += step;
1292       if (first_entry + MAX_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
1293         first_entry = MAX(0, MAX_SCORE_ENTRIES - MAX_MENU_ENTRIES_ON_SCREEN);
1294
1295       drawHallOfFameList(first_entry, highlight_position);
1296       return;
1297     }
1298   }
1299
1300   if (button_released)
1301   {
1302     FadeSound(SND_HALLOFFAME);
1303     game_status = MAINMENU;
1304     DrawMainMenu();
1305   }
1306
1307   BackToFront();
1308
1309   if (game_status == HALLOFFAME)
1310     DoAnimation();
1311 }
1312
1313 static void execSetupInput()
1314 {
1315   game_status = SETUPINPUT;
1316   DrawSetupInputScreen();
1317 }
1318
1319 static void execExitSetup()
1320 {
1321   game_status = MAINMENU;
1322   DrawMainMenu();
1323 }
1324
1325 static void execSaveAndExitSetup()
1326 {
1327   SaveSetup();
1328   execExitSetup();
1329 }
1330
1331 static struct TokenInfo setup_info[] =
1332 {
1333   { TYPE_SWITCH,        &setup.sound,           "Sound:",       },
1334   { TYPE_SWITCH,        &setup.sound_loops,     " Sound Loops:" },
1335   { TYPE_SWITCH,        &setup.sound_music,     " Game Music:"  },
1336 #if 0
1337   { TYPE_SWITCH,        &setup.toons,           "Toons:"        },
1338   { TYPE_SWITCH,        &setup.double_buffering,"Buffered gfx:" },
1339 #endif
1340   { TYPE_SWITCH,        &setup.scroll_delay,    "Scroll Delay:" },
1341   { TYPE_SWITCH,        &setup.soft_scrolling,  "Soft Scroll.:" },
1342 #if 0
1343   { TYPE_SWITCH,        &setup.fading,          "Fading:"       },
1344 #endif
1345   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen:"   },
1346   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Doors:"  },
1347   { TYPE_SWITCH,        &setup.autorecord,      "Auto-Record:"  },
1348   { TYPE_SWITCH,        &setup.team_mode,       "Team-Mode:"    },
1349   { TYPE_SWITCH,        &setup.handicap,        "Handicap:"     },
1350   { TYPE_SWITCH,        &setup.time_limit,      "Timelimit:"    },
1351   { TYPE_ENTER_MENU,    execSetupInput,         "Input Devices" },
1352   { TYPE_EMPTY,         NULL,                   ""              },
1353   { TYPE_LEAVE_MENU,    execExitSetup,          "Exit"          },
1354   { TYPE_LEAVE_MENU,    execSaveAndExitSetup,   "Save and exit" },
1355   { 0,                  NULL,                   NULL            }
1356 };
1357 static int num_setup_info = 0;
1358
1359 static void drawSetupValue(struct TokenInfo *si, int pos)
1360 {
1361   int xpos = MENU_SCREEN_VALUE_XPOS;
1362   int ypos = MENU_SCREEN_START_YPOS + pos;
1363   int value_color = FC_YELLOW;
1364   char *value_string = getSetupValue(si[pos].type & ~TYPE_GHOSTED,
1365                                      si[pos].value);
1366
1367   if (si[pos].type & TYPE_SWITCH ||
1368       si[pos].type & TYPE_YES_NO)
1369   {
1370     boolean value = *(boolean *)(si[pos].value);
1371     int value_length = 3;
1372
1373     if (!value)
1374       value_color = FC_BLUE;
1375
1376     if (strlen(value_string) < value_length)
1377       strcat(value_string, " ");
1378   }
1379
1380   DrawText(SX + xpos * 32, SY + ypos * 32, value_string, FS_BIG, value_color);
1381 }
1382
1383 void DrawSetupScreen()
1384 {
1385   int i;
1386
1387   UnmapAllGadgets();
1388   CloseDoor(DOOR_CLOSE_2);
1389   ClearWindow();
1390
1391   DrawText(SX + 16, SY + 16, "SETUP",FS_BIG,FC_YELLOW);
1392
1393   for(i=0; setup_info[i].type != 0 && i < MAX_MENU_ENTRIES_ON_SCREEN; i++)
1394   {
1395     void *value_ptr = setup_info[i].value;
1396     int ypos = MENU_SCREEN_START_YPOS + i;
1397
1398     /* set some entries to "unchangeable" according to other variables */
1399     if ((value_ptr == &setup.sound       && !audio.sound_available) ||
1400         (value_ptr == &setup.sound_loops && !audio.loops_available) ||
1401         (value_ptr == &setup.sound_music && !audio.music_available) ||
1402         (value_ptr == &setup.sound_music && !audio.music_available) ||
1403         (value_ptr == &setup.fullscreen  && !video.fullscreen_available))
1404       setup_info[i].type |= TYPE_GHOSTED;
1405
1406     DrawText(SX + 32, SY + ypos * 32, setup_info[i].text, FS_BIG, FC_GREEN);
1407
1408     if (setup_info[i].type & TYPE_ENTER_MENU)
1409         initCursor(i, GFX_ARROW_BLUE_RIGHT);
1410     else if (setup_info[i].type & TYPE_LEAVE_MENU)
1411         initCursor(i, GFX_ARROW_BLUE_LEFT);
1412     else if (setup_info[i].type != TYPE_EMPTY)
1413       initCursor(i, GFX_KUGEL_BLAU);
1414
1415     if (setup_info[i].type & TYPE_BOOLEAN_STYLE)
1416       drawSetupValue(setup_info, i);
1417
1418     num_setup_info++;
1419   }
1420
1421   FadeToFront();
1422   InitAnimation();
1423   HandleSetupScreen(0,0,0,0,MB_MENU_INITIALIZE);
1424 }
1425
1426
1427 #if 1
1428
1429 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
1430 {
1431   static int choice = 0;
1432   static int redraw = TRUE;
1433   int x = (mx - SX) / 32;
1434   int y = (my - SY) / 32 - MENU_SCREEN_START_YPOS;
1435   int pos_start  = SETUP_SCREEN_POS_START;
1436   int pos_empty1 = SETUP_SCREEN_POS_EMPTY1;
1437   int pos_empty2 = SETUP_SCREEN_POS_EMPTY2;
1438   int pos_end    = SETUP_SCREEN_POS_END;
1439
1440   if (button == MB_MENU_INITIALIZE)
1441     redraw = TRUE;
1442
1443   if (redraw)
1444   {
1445     drawCursor(choice, FC_RED);
1446     redraw = FALSE;
1447   }
1448
1449   if (button == MB_MENU_INITIALIZE)
1450     return;
1451
1452   if (dx || dy)
1453   {
1454     x = 0;
1455     y = choice;
1456
1457     if (dx)
1458     {
1459       int type = (dx < 0 ? TYPE_LEAVE_MENU : TYPE_ENTER_MENU);
1460
1461       if (!(setup_info[choice].type & TYPE_ENTER_OR_LEAVE_MENU) ||
1462           setup_info[choice].type == type)
1463         button = MB_MENU_CHOICE;
1464     }
1465     else if (dy)
1466       y = choice + dy;
1467
1468     /* stay in valid range */
1469     if (y < 0)
1470       y = 0;
1471     else if (y > num_setup_info - 1)
1472       y = num_setup_info - 1;
1473
1474     /* jump to next non-empty menu entry (up or down) */
1475     if (setup_info[y].type == TYPE_EMPTY)
1476       while (y > 0 && y < num_setup_info - 1 &&
1477              setup_info[y].type == TYPE_EMPTY)
1478         y += dy;
1479   }
1480
1481   if (!mx && !my && !dx && !dy)
1482   {
1483     x = 0;
1484     y = choice;
1485   }
1486
1487   if (x == 0 && y >= 0 && y < num_setup_info &&
1488       setup_info[y].type != TYPE_EMPTY)
1489   {
1490     if (button)
1491     {
1492       if (y != choice)
1493       {
1494         drawCursor(y, FC_RED);
1495         drawCursor(choice, FC_BLUE);
1496         choice = y;
1497       }
1498     }
1499     else if (!(setup_info[y].type & TYPE_GHOSTED))
1500     {
1501       if (setup_info[y].type & TYPE_BOOLEAN_STYLE)
1502       {
1503         boolean new_value = !*(boolean *)(setup_info[y].value);
1504
1505         *(boolean *)setup_info[y].value = new_value;
1506
1507         drawSetupValue(setup_info, y);
1508       }
1509       else if (setup_info[y].type & TYPE_ENTER_OR_LEAVE_MENU)
1510       {
1511         void (*menu_callback_function)(void) = setup_info[choice].value;
1512
1513         menu_callback_function();
1514         redraw = TRUE;
1515       }
1516     }
1517   }
1518
1519   BackToFront();
1520
1521   if (game_status == SETUP)
1522     DoAnimation();
1523
1524   return;
1525
1526
1527
1528   if (x == 0 && y >= pos_start && y <= pos_end &&
1529       !(y >= pos_empty1 && y <= pos_empty2))
1530   {
1531     if (button)
1532     {
1533       if (y != choice)
1534       {
1535         drawCursor(y, FC_RED);
1536         drawCursor(choice, FC_BLUE);
1537       }
1538       choice = y;
1539     }
1540     else
1541     {
1542       int yy = MENU_SCREEN_START_YPOS + y;
1543
1544       if (y == 0 && audio.sound_available)
1545       {
1546         if (setup.sound)
1547         {
1548           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1549           DrawText(SX+14*32, SY+(yy+1)*32,"off",FS_BIG,FC_BLUE);
1550           DrawText(SX+14*32, SY+(yy+2)*32,"off",FS_BIG,FC_BLUE);
1551           setup.sound_loops = FALSE;
1552           setup.sound_music = FALSE;
1553         }
1554         else
1555           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1556         setup.sound = !setup.sound;
1557       }
1558       else if (y == 1 && audio.loops_available)
1559       {
1560         if (setup.sound_loops)
1561           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1562         else
1563         {
1564           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1565           DrawText(SX+14*32, SY+(yy-1)*32,"on ",FS_BIG,FC_YELLOW);
1566           setup.sound = TRUE;
1567         }
1568         setup.sound_loops = !setup.sound_loops;
1569       }
1570       else if (y == 2 && audio.loops_available)
1571       {
1572         if (setup.sound_music)
1573           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1574         else
1575         {
1576           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1577           DrawText(SX+14*32, SY+(yy-2)*32,"on ",FS_BIG,FC_YELLOW);
1578           setup.sound = TRUE;
1579         }
1580         setup.sound_music = !setup.sound_music;
1581       }
1582
1583 #if 0
1584       else if (y == 3)
1585       {
1586         if (setup.toons)
1587           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1588         else
1589           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1590         setup.toons = !setup.toons;
1591       }
1592       else if (y == 4)
1593       {
1594 #if 0
1595         if (setup.double_buffering)
1596           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1597         else
1598           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1599         setup.double_buffering = !setup.double_buffering;
1600         setup.direct_draw = !setup.double_buffering;
1601 #else
1602         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1603         setup.double_buffering = TRUE;
1604         setup.direct_draw = !setup.double_buffering;
1605 #endif
1606       }
1607 #endif
1608
1609       else if (y == 3)
1610       {
1611         if (setup.scroll_delay)
1612           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1613         else
1614           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1615         setup.scroll_delay = !setup.scroll_delay;
1616       }
1617       else if (y == 4)
1618       {
1619         if (setup.soft_scrolling)
1620           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1621         else
1622           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1623         setup.soft_scrolling = !setup.soft_scrolling;
1624       }
1625 #if 0
1626       else if (y == 5)
1627       {
1628         if (setup.fading)
1629           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1630         else
1631           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1632         setup.fading = !setup.fading;
1633       }
1634 #endif
1635       else if (y == 5 && video.fullscreen_available)
1636       {
1637         if (setup.fullscreen)
1638           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1639         else
1640           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1641         setup.fullscreen = !setup.fullscreen;
1642       }
1643       else if (y == 6)
1644       {
1645         if (setup.quick_doors)
1646           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1647         else
1648           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1649         setup.quick_doors = !setup.quick_doors;
1650       }
1651       else if (y == 7)
1652       {
1653         if (setup.autorecord)
1654           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1655         else
1656           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1657         setup.autorecord = !setup.autorecord;
1658       }
1659       else if (y == 8)
1660       {
1661         if (setup.team_mode)
1662           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1663         else
1664           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1665         setup.team_mode = !setup.team_mode;
1666       }
1667       else if (y == 9)
1668       {
1669         if (setup.handicap)
1670           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1671         else
1672           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1673         setup.handicap = !setup.handicap;
1674       }
1675       else if (y == 10)
1676       {
1677         if (setup.time_limit)
1678           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1679         else
1680           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1681         setup.time_limit = !setup.time_limit;
1682       }
1683       else if (y == 11)
1684       {
1685         game_status = SETUPINPUT;
1686         DrawSetupInputScreen();
1687         redraw = TRUE;
1688       }
1689       else if (y == pos_end - 1 || y == pos_end)
1690       {
1691         if (y == pos_end)
1692           SaveSetup();
1693
1694         game_status = MAINMENU;
1695         DrawMainMenu();
1696         redraw = TRUE;
1697       }
1698     }
1699   }
1700   BackToFront();
1701
1702   if (game_status==SETUP)
1703     DoAnimation();
1704 }
1705
1706 #else
1707
1708 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
1709 {
1710   static int choice = 0;
1711   static int redraw = TRUE;
1712   int x = (mx - SX) / 32;
1713   int y = (my - SY) / 32 - MENU_SCREEN_START_YPOS;
1714   int pos_start  = SETUP_SCREEN_POS_START;
1715   int pos_empty1 = SETUP_SCREEN_POS_EMPTY1;
1716   int pos_empty2 = SETUP_SCREEN_POS_EMPTY2;
1717   int pos_end    = SETUP_SCREEN_POS_END;
1718
1719   if (button == MB_MENU_INITIALIZE)
1720     redraw = TRUE;
1721
1722   if (redraw)
1723   {
1724     drawCursor(choice, FC_RED);
1725     redraw = FALSE;
1726   }
1727
1728   if (button == MB_MENU_INITIALIZE)
1729     return;
1730
1731   if (dx || dy)
1732   {
1733     if (dy)
1734     {
1735       x = 0;
1736       y = choice + dy;
1737     }
1738     else
1739       x = y = -1;
1740
1741     if (y >= pos_empty1 && y <= pos_empty2)
1742       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
1743
1744     if (y < pos_start)
1745       y = pos_start;
1746     else if (y > pos_end)
1747       y = pos_end;
1748   }
1749
1750   if (!mx && !my && !dx && !dy)
1751   {
1752     x = 0;
1753     y = choice;
1754   }
1755
1756   if (dx == 1 && choice == 11)
1757   {
1758     game_status = SETUPINPUT;
1759     DrawSetupInputScreen();
1760     redraw = TRUE;
1761   }
1762
1763   if (x == 0 && y >= pos_start && y <= pos_end &&
1764       !(y >= pos_empty1 && y <= pos_empty2))
1765   {
1766     if (button)
1767     {
1768       if (y != choice)
1769       {
1770         drawCursor(y, FC_RED);
1771         drawCursor(choice, FC_BLUE);
1772       }
1773       choice = y;
1774     }
1775     else
1776     {
1777       int yy = MENU_SCREEN_START_YPOS + y;
1778
1779       if (y == 0 && audio.sound_available)
1780       {
1781         if (setup.sound)
1782         {
1783           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1784           DrawText(SX+14*32, SY+(yy+1)*32,"off",FS_BIG,FC_BLUE);
1785           DrawText(SX+14*32, SY+(yy+2)*32,"off",FS_BIG,FC_BLUE);
1786           setup.sound_loops = FALSE;
1787           setup.sound_music = FALSE;
1788         }
1789         else
1790           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1791         setup.sound = !setup.sound;
1792       }
1793       else if (y == 1 && audio.loops_available)
1794       {
1795         if (setup.sound_loops)
1796           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1797         else
1798         {
1799           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1800           DrawText(SX+14*32, SY+(yy-1)*32,"on ",FS_BIG,FC_YELLOW);
1801           setup.sound = TRUE;
1802         }
1803         setup.sound_loops = !setup.sound_loops;
1804       }
1805       else if (y == 2 && audio.loops_available)
1806       {
1807         if (setup.sound_music)
1808           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1809         else
1810         {
1811           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1812           DrawText(SX+14*32, SY+(yy-2)*32,"on ",FS_BIG,FC_YELLOW);
1813           setup.sound = TRUE;
1814         }
1815         setup.sound_music = !setup.sound_music;
1816       }
1817
1818 #if 0
1819       else if (y == 3)
1820       {
1821         if (setup.toons)
1822           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1823         else
1824           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1825         setup.toons = !setup.toons;
1826       }
1827       else if (y == 4)
1828       {
1829 #if 0
1830         if (setup.double_buffering)
1831           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1832         else
1833           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1834         setup.double_buffering = !setup.double_buffering;
1835         setup.direct_draw = !setup.double_buffering;
1836 #else
1837         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1838         setup.double_buffering = TRUE;
1839         setup.direct_draw = !setup.double_buffering;
1840 #endif
1841       }
1842 #endif
1843
1844       else if (y == 3)
1845       {
1846         if (setup.scroll_delay)
1847           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1848         else
1849           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1850         setup.scroll_delay = !setup.scroll_delay;
1851       }
1852       else if (y == 4)
1853       {
1854         if (setup.soft_scrolling)
1855           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1856         else
1857           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1858         setup.soft_scrolling = !setup.soft_scrolling;
1859       }
1860 #if 0
1861       else if (y == 5)
1862       {
1863         if (setup.fading)
1864           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1865         else
1866           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1867         setup.fading = !setup.fading;
1868       }
1869 #endif
1870       else if (y == 5 && video.fullscreen_available)
1871       {
1872         if (setup.fullscreen)
1873           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1874         else
1875           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1876         setup.fullscreen = !setup.fullscreen;
1877       }
1878       else if (y == 6)
1879       {
1880         if (setup.quick_doors)
1881           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1882         else
1883           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1884         setup.quick_doors = !setup.quick_doors;
1885       }
1886       else if (y == 7)
1887       {
1888         if (setup.autorecord)
1889           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1890         else
1891           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1892         setup.autorecord = !setup.autorecord;
1893       }
1894       else if (y == 8)
1895       {
1896         if (setup.team_mode)
1897           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1898         else
1899           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1900         setup.team_mode = !setup.team_mode;
1901       }
1902       else if (y == 9)
1903       {
1904         if (setup.handicap)
1905           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1906         else
1907           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1908         setup.handicap = !setup.handicap;
1909       }
1910       else if (y == 10)
1911       {
1912         if (setup.time_limit)
1913           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1914         else
1915           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1916         setup.time_limit = !setup.time_limit;
1917       }
1918       else if (y == 11)
1919       {
1920         game_status = SETUPINPUT;
1921         DrawSetupInputScreen();
1922         redraw = TRUE;
1923       }
1924       else if (y == pos_end - 1 || y == pos_end)
1925       {
1926         if (y == pos_end)
1927           SaveSetup();
1928
1929         game_status = MAINMENU;
1930         DrawMainMenu();
1931         redraw = TRUE;
1932       }
1933     }
1934   }
1935   BackToFront();
1936
1937   if (game_status==SETUP)
1938     DoAnimation();
1939 }
1940
1941 #endif
1942
1943
1944 void DrawSetupInputScreen()
1945 {
1946   ClearWindow();
1947   DrawText(SX+16, SY+16, "SETUP INPUT", FS_BIG, FC_YELLOW);
1948
1949   initCursor(0, GFX_KUGEL_BLAU);
1950   initCursor(1, GFX_KUGEL_BLAU);
1951   initCursor(2, GFX_ARROW_BLUE_RIGHT);
1952   initCursor(13, GFX_KUGEL_BLAU);
1953
1954   DrawGraphic(10, MENU_SCREEN_START_YPOS, GFX_ARROW_BLUE_LEFT);
1955   DrawGraphic(12, MENU_SCREEN_START_YPOS, GFX_ARROW_BLUE_RIGHT);
1956
1957   DrawText(SX+32, SY+2*32, "Player:", FS_BIG, FC_GREEN);
1958   DrawText(SX+32, SY+3*32, "Device:", FS_BIG, FC_GREEN);
1959   DrawText(SX+32, SY+15*32, "Exit", FS_BIG, FC_GREEN);
1960
1961   DeactivateJoystickForCalibration();
1962   DrawTextFCentered(SYSIZE - 20, FC_BLUE,
1963                     "Joysticks deactivated on this screen");
1964
1965   HandleSetupInputScreen(0,0, 0,0, MB_MENU_INITIALIZE);
1966   FadeToFront();
1967   InitAnimation();
1968 }
1969
1970 static void setJoystickDeviceToNr(char *device_name, int device_nr)
1971 {
1972   if (device_name == NULL)
1973     return;
1974
1975   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
1976     device_nr = 0;
1977
1978   if (strlen(device_name) > 1)
1979   {
1980     char c1 = device_name[strlen(device_name) - 1];
1981     char c2 = device_name[strlen(device_name) - 2];
1982
1983     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
1984       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
1985   }
1986   else
1987     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
1988             strlen(device_name));
1989 }
1990
1991 static void drawPlayerSetupInputInfo(int player_nr)
1992 {
1993   int i;
1994   static struct SetupKeyboardInfo custom_key;
1995   static struct
1996   {
1997     Key *key;
1998     char *text;
1999   } custom[] =
2000   {
2001     { &custom_key.left,  "Joystick Left"  },
2002     { &custom_key.right, "Joystick Right" },
2003     { &custom_key.up,    "Joystick Up"    },
2004     { &custom_key.down,  "Joystick Down"  },
2005     { &custom_key.snap,  "Button 1"       },
2006     { &custom_key.bomb,  "Button 2"       }
2007   };
2008   static char *joystick_name[MAX_PLAYERS] =
2009   {
2010     "Joystick1",
2011     "Joystick2",
2012     "Joystick3",
2013     "Joystick4"
2014   };
2015
2016   custom_key = setup.input[player_nr].key;
2017
2018   DrawText(SX+11*32, SY+2*32, int2str(player_nr + 1, 1), FS_BIG, FC_RED);
2019   DrawGraphic(8, 2, GFX_SPIELER1 + player_nr);
2020
2021   if (setup.input[player_nr].use_joystick)
2022   {
2023     char *device_name = setup.input[player_nr].joy.device_name;
2024
2025     DrawText(SX+8*32, SY+3*32,
2026              joystick_name[getJoystickNrFromDeviceName(device_name)],
2027              FS_BIG, FC_YELLOW);
2028     DrawText(SX+32, SY+4*32, "Calibrate", FS_BIG, FC_GREEN);
2029   }
2030   else
2031   {
2032     DrawText(SX+8*32, SY+3*32, "Keyboard ", FS_BIG, FC_YELLOW);
2033     DrawText(SX+32, SY+4*32, "Customize", FS_BIG, FC_GREEN);
2034   }
2035
2036   DrawText(SX+32, SY+5*32, "Actual Settings:", FS_BIG, FC_GREEN);
2037   DrawGraphic(1, 6, GFX_ARROW_BLUE_LEFT);
2038   DrawGraphic(1, 7, GFX_ARROW_BLUE_RIGHT);
2039   DrawGraphic(1, 8, GFX_ARROW_BLUE_UP);
2040   DrawGraphic(1, 9, GFX_ARROW_BLUE_DOWN);
2041   DrawText(SX+2*32, SY+6*32, ":", FS_BIG, FC_BLUE);
2042   DrawText(SX+2*32, SY+7*32, ":", FS_BIG, FC_BLUE);
2043   DrawText(SX+2*32, SY+8*32, ":", FS_BIG, FC_BLUE);
2044   DrawText(SX+2*32, SY+9*32, ":", FS_BIG, FC_BLUE);
2045   DrawText(SX+32, SY+10*32, "Snap Field:", FS_BIG, FC_BLUE);
2046   DrawText(SX+32, SY+12*32, "Place Bomb:", FS_BIG, FC_BLUE);
2047
2048   for (i=0; i<6; i++)
2049   {
2050     int ypos = 6 + i + (i > 3 ? i-3 : 0);
2051
2052     DrawText(SX + 3*32, SY + ypos*32,
2053              "              ", FS_BIG, FC_YELLOW);
2054     DrawText(SX + 3*32, SY + ypos*32,
2055              (setup.input[player_nr].use_joystick ?
2056               custom[i].text :
2057               getKeyNameFromKey(*custom[i].key)),
2058              FS_BIG, FC_YELLOW);
2059   }
2060 }
2061
2062 void HandleSetupInputScreen(int mx, int my, int dx, int dy, int button)
2063 {
2064   static int choice = 0;
2065   static int player_nr = 0;
2066   static int redraw = TRUE;
2067   int x = (mx - SX) / 32;
2068   int y = (my - SY) / 32 - MENU_SCREEN_START_YPOS;
2069   int pos_start  = SETUPINPUT_SCREEN_POS_START;
2070   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
2071   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
2072   int pos_end    = SETUPINPUT_SCREEN_POS_END;
2073
2074   if (button == MB_MENU_INITIALIZE)
2075   {
2076     drawPlayerSetupInputInfo(player_nr);
2077     redraw = TRUE;
2078   }
2079
2080   if (redraw)
2081   {
2082     drawCursor(choice, FC_RED);
2083     redraw = FALSE;
2084   }
2085
2086   if (button == MB_MENU_INITIALIZE)
2087     return;
2088
2089   if (dx || dy)
2090   {
2091     if (dx && choice == 0)
2092     {
2093       x = (dx < 0 ? 10 : 12);
2094       y = 0;
2095     }
2096     else if (dx && choice == 1)
2097     {
2098       button = MB_MENU_CHOICE;
2099       x = 0;
2100       y = 1;
2101     }
2102     else if (dy)
2103     {
2104       x = 0;
2105       y = choice + dy;
2106     }
2107     else
2108       x = y = -1;
2109
2110     if (y >= pos_empty1 && y <= pos_empty2)
2111       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
2112
2113     if (y < pos_start)
2114       y = pos_start;
2115     else if (y > pos_end)
2116       y = pos_end;
2117   }
2118
2119   if (!mx && !my && !dx && !dy)
2120   {
2121     x = 0;
2122     y = choice;
2123   }
2124
2125   if (y == 0 && ((x == 0 && !button) || ((x == 10 || x == 12) && button)))
2126   {
2127     static unsigned long delay = 0;
2128
2129     if (!DelayReached(&delay, GADGET_FRAME_DELAY))
2130       goto out;
2131
2132     player_nr = (player_nr + (x == 10 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
2133
2134     drawPlayerSetupInputInfo(player_nr);
2135   }
2136   else if (x == 0 && y >= pos_start && y <= pos_end &&
2137            !(y >= pos_empty1 && y <= pos_empty2))
2138   {
2139     if (button)
2140     {
2141       if (y != choice)
2142       {
2143         drawCursor(y, FC_RED);
2144         drawCursor(choice, FC_BLUE);
2145       }
2146       choice = y;
2147     }
2148     else
2149     {
2150       if (y == 1)
2151       {
2152         char *device_name = setup.input[player_nr].joy.device_name;
2153
2154         if (!setup.input[player_nr].use_joystick)
2155         {
2156           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
2157
2158           setJoystickDeviceToNr(device_name, new_device_nr);
2159           setup.input[player_nr].use_joystick = TRUE;
2160         }
2161         else
2162         {
2163           int device_nr = getJoystickNrFromDeviceName(device_name);
2164           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
2165
2166           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
2167             setup.input[player_nr].use_joystick = FALSE;
2168           else
2169             setJoystickDeviceToNr(device_name, new_device_nr);
2170         }
2171
2172         drawPlayerSetupInputInfo(player_nr);
2173       }
2174       else if (y == 2)
2175       {
2176         if (setup.input[player_nr].use_joystick)
2177         {
2178           InitJoysticks();
2179           game_status = CALIBRATION;
2180           CalibrateJoystick(player_nr);
2181           game_status = SETUPINPUT;
2182         }
2183         else
2184           CustomizeKeyboard(player_nr);
2185
2186         redraw = TRUE;
2187       }
2188       else if (y == pos_end)
2189       {
2190         InitJoysticks();
2191
2192         game_status = SETUP;
2193         DrawSetupScreen();
2194         redraw = TRUE;
2195       }
2196     }
2197   }
2198   BackToFront();
2199
2200   out:
2201
2202   if (game_status == SETUPINPUT)
2203     DoAnimation();
2204 }
2205
2206 void CustomizeKeyboard(int player_nr)
2207 {
2208   int i;
2209   int step_nr;
2210   boolean finished = FALSE;
2211   static struct SetupKeyboardInfo custom_key;
2212   static struct
2213   {
2214     Key *key;
2215     char *text;
2216   } customize_step[] =
2217   {
2218     { &custom_key.left,  "Move Left"  },
2219     { &custom_key.right, "Move Right" },
2220     { &custom_key.up,    "Move Up"    },
2221     { &custom_key.down,  "Move Down"  },
2222     { &custom_key.snap,  "Snap Field" },
2223     { &custom_key.bomb,  "Place Bomb" }
2224   };
2225
2226   /* read existing key bindings from player setup */
2227   custom_key = setup.input[player_nr].key;
2228
2229   ClearWindow();
2230   DrawText(SX + 16, SY + 16, "Keyboard Input", FS_BIG, FC_YELLOW);
2231
2232   BackToFront();
2233   InitAnimation();
2234
2235   step_nr = 0;
2236   DrawText(SX, SY + (2+2*step_nr)*32,
2237            customize_step[step_nr].text, FS_BIG, FC_RED);
2238   DrawText(SX, SY + (2+2*step_nr+1)*32,
2239            "Key:", FS_BIG, FC_RED);
2240   DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
2241            getKeyNameFromKey(*customize_step[step_nr].key),
2242            FS_BIG, FC_BLUE);
2243
2244   while(!finished)
2245   {
2246     if (PendingEvent())         /* got event */
2247     {
2248       Event event;
2249
2250       NextEvent(&event);
2251
2252       switch(event.type)
2253       {
2254         case EVENT_KEYPRESS:
2255           {
2256             Key key = GetEventKey((KeyEvent *)&event, TRUE);
2257
2258             if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
2259             {
2260               finished = TRUE;
2261               break;
2262             }
2263
2264             /* all keys configured -- wait for "Escape" or "Return" key */
2265             if (step_nr == 6)
2266               break;
2267
2268             /* press 'Enter' to keep the existing key binding */
2269             if (key == KSYM_Return)
2270               key = *customize_step[step_nr].key;
2271
2272             /* check if key already used */
2273             for (i=0; i<step_nr; i++)
2274               if (*customize_step[i].key == key)
2275                 break;
2276             if (i < step_nr)
2277               break;
2278
2279             /* got new key binding */
2280             *customize_step[step_nr].key = key;
2281             DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
2282                      "             ", FS_BIG, FC_YELLOW);
2283             DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
2284                      getKeyNameFromKey(key), FS_BIG, FC_YELLOW);
2285             step_nr++;
2286
2287             /* un-highlight last query */
2288             DrawText(SX, SY+(2+2*(step_nr-1))*32,
2289                      customize_step[step_nr-1].text, FS_BIG, FC_GREEN);
2290             DrawText(SX, SY+(2+2*(step_nr-1)+1)*32,
2291                      "Key:", FS_BIG, FC_GREEN);
2292
2293             /* press 'Enter' to leave */
2294             if (step_nr == 6)
2295             {
2296               DrawText(SX + 16, SY + 15*32+16,
2297                        "Press Enter", FS_BIG, FC_YELLOW);
2298               break;
2299             }
2300
2301             /* query next key binding */
2302             DrawText(SX, SY+(2+2*step_nr)*32,
2303                      customize_step[step_nr].text, FS_BIG, FC_RED);
2304             DrawText(SX, SY+(2+2*step_nr+1)*32,
2305                      "Key:", FS_BIG, FC_RED);
2306             DrawText(SX + 4*32, SY+(2+2*step_nr+1)*32,
2307                      getKeyNameFromKey(*customize_step[step_nr].key),
2308                      FS_BIG, FC_BLUE);
2309           }
2310           break;
2311
2312         case EVENT_KEYRELEASE:
2313           key_joystick_mapping = 0;
2314           break;
2315
2316         default:
2317           HandleOtherEvents(&event);
2318           break;
2319       }
2320     }
2321
2322     BackToFront();
2323     DoAnimation();
2324
2325     /* don't eat all CPU time */
2326     Delay(10);
2327   }
2328
2329   /* write new key bindings back to player setup */
2330   setup.input[player_nr].key = custom_key;
2331
2332   StopAnimation();
2333   DrawSetupInputScreen();
2334 }
2335
2336 static boolean CalibrateJoystickMain(int player_nr)
2337 {
2338   int new_joystick_xleft = JOYSTICK_XMIDDLE;
2339   int new_joystick_xright = JOYSTICK_XMIDDLE;
2340   int new_joystick_yupper = JOYSTICK_YMIDDLE;
2341   int new_joystick_ylower = JOYSTICK_YMIDDLE;
2342   int new_joystick_xmiddle, new_joystick_ymiddle;
2343
2344   int joystick_fd = joystick.fd[player_nr];
2345   int x, y, last_x, last_y, xpos = 8, ypos = 3;
2346   boolean check[3][3];
2347   int check_remaining = 3 * 3;
2348   int joy_x, joy_y;
2349   int joy_value;
2350   int result = -1;
2351
2352   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
2353     return FALSE;
2354
2355   if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
2356     return FALSE;
2357
2358   ClearWindow();
2359
2360   for(y=0; y<3; y++)
2361   {
2362     for(x=0; x<3; x++)
2363     {
2364       check[x][y] = FALSE;
2365       DrawGraphic(xpos + x - 1, ypos + y - 1, GFX_KUGEL_BLAU);
2366     }
2367   }
2368
2369   DrawText(SX,      SY +  6 * 32, " ROTATE JOYSTICK ", FS_BIG, FC_YELLOW);
2370   DrawText(SX,      SY +  7 * 32, "IN ALL DIRECTIONS", FS_BIG, FC_YELLOW);
2371   DrawText(SX + 16, SY +  9 * 32, "  IF ALL BALLS  ",  FS_BIG, FC_YELLOW);
2372   DrawText(SX,      SY + 10 * 32, "   ARE YELLOW,   ", FS_BIG, FC_YELLOW);
2373   DrawText(SX,      SY + 11 * 32, " CENTER JOYSTICK ", FS_BIG, FC_YELLOW);
2374   DrawText(SX,      SY + 12 * 32, "       AND       ", FS_BIG, FC_YELLOW);
2375   DrawText(SX,      SY + 13 * 32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
2376
2377   joy_value = Joystick(player_nr);
2378   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
2379   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
2380
2381   /* eventually uncalibrated center position (joystick could be uncentered) */
2382   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
2383     return FALSE;
2384
2385   new_joystick_xmiddle = joy_x;
2386   new_joystick_ymiddle = joy_y;
2387
2388   DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_ROT);
2389   BackToFront();
2390
2391   while(Joystick(player_nr) & JOY_BUTTON);      /* wait for released button */
2392   InitAnimation();
2393
2394   while(result < 0)
2395   {
2396     if (PendingEvent())         /* got event */
2397     {
2398       Event event;
2399
2400       NextEvent(&event);
2401
2402       switch(event.type)
2403       {
2404         case EVENT_KEYPRESS:
2405           switch(GetEventKey((KeyEvent *)&event, TRUE))
2406           {
2407             case KSYM_Return:
2408               if (check_remaining == 0)
2409                 result = 1;
2410               break;
2411
2412             case KSYM_Escape:
2413               result = 0;
2414               break;
2415
2416             default:
2417               break;
2418           }
2419           break;
2420
2421         case EVENT_KEYRELEASE:
2422           key_joystick_mapping = 0;
2423           break;
2424
2425         default:
2426           HandleOtherEvents(&event);
2427           break;
2428       }
2429     }
2430
2431     if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
2432       return FALSE;
2433
2434     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
2435     new_joystick_xright = MAX(new_joystick_xright, joy_x);
2436     new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
2437     new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
2438
2439     setup.input[player_nr].joy.xleft = new_joystick_xleft;
2440     setup.input[player_nr].joy.yupper = new_joystick_yupper;
2441     setup.input[player_nr].joy.xright = new_joystick_xright;
2442     setup.input[player_nr].joy.ylower = new_joystick_ylower;
2443     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
2444     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
2445
2446     CheckJoystickData();
2447
2448     joy_value = Joystick(player_nr);
2449
2450     if (joy_value & JOY_BUTTON && check_remaining == 0)
2451       result = 1;
2452
2453     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
2454     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
2455
2456     if (x != last_x || y != last_y)
2457     {
2458       DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_GELB);
2459       DrawGraphic(xpos + x,      ypos + y,      GFX_KUGEL_ROT);
2460
2461       last_x = x;
2462       last_y = y;
2463
2464       if (check_remaining > 0 && !check[x+1][y+1])
2465       {
2466         check[x+1][y+1] = TRUE;
2467         check_remaining--;
2468       }
2469
2470 #if 0
2471 #ifdef DEBUG
2472       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
2473              setup.input[player_nr].joy.xleft,
2474              setup.input[player_nr].joy.xmiddle,
2475              setup.input[player_nr].joy.xright);
2476       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
2477              setup.input[player_nr].joy.yupper,
2478              setup.input[player_nr].joy.ymiddle,
2479              setup.input[player_nr].joy.ylower);
2480 #endif
2481 #endif
2482
2483     }
2484
2485     BackToFront();
2486     DoAnimation();
2487
2488     /* don't eat all CPU time */
2489     Delay(10);
2490   }
2491
2492   /* calibrated center position (joystick should now be centered) */
2493   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
2494     return FALSE;
2495
2496   new_joystick_xmiddle = joy_x;
2497   new_joystick_ymiddle = joy_y;
2498
2499   StopAnimation();
2500
2501   DrawSetupInputScreen();
2502
2503   /* wait until the last pressed button was released */
2504   while (Joystick(player_nr) & JOY_BUTTON)
2505   {
2506     if (PendingEvent())         /* got event */
2507     {
2508       Event event;
2509
2510       NextEvent(&event);
2511       HandleOtherEvents(&event);
2512
2513       Delay(10);
2514     }
2515   }
2516
2517   return TRUE;
2518 }
2519
2520 void CalibrateJoystick(int player_nr)
2521 {
2522   if (!CalibrateJoystickMain(player_nr))
2523   {
2524     ClearWindow();
2525
2526     DrawText(SX + 16, SY + 6*32, "  JOYSTICK NOT  ",  FS_BIG, FC_YELLOW);
2527     DrawText(SX,      SY + 7*32, "    AVAILABLE    ", FS_BIG, FC_YELLOW);
2528     BackToFront();
2529     Delay(2000);        /* show error message for two seconds */
2530   }
2531 }
2532
2533 void HandleGameActions()
2534 {
2535   if (game_status != PLAYING)
2536     return;
2537
2538   if (local_player->LevelSolved)
2539     GameWon();
2540
2541   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
2542     TapeStop();
2543
2544   GameActions();
2545
2546   BackToFront();
2547 }
2548
2549 /* ---------- new screen button stuff -------------------------------------- */
2550
2551 /* graphic position and size values for buttons and scrollbars */
2552 #define SC_SCROLLBUTTON_XPOS            64
2553 #define SC_SCROLLBUTTON_YPOS            0
2554 #define SC_SCROLLBAR_XPOS               0
2555 #define SC_SCROLLBAR_YPOS               64
2556
2557 #define SC_SCROLLBUTTON_XSIZE           32
2558 #define SC_SCROLLBUTTON_YSIZE           32
2559
2560 #define SC_SCROLL_UP_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
2561 #define SC_SCROLL_UP_YPOS               SC_SCROLLBUTTON_YSIZE
2562 #define SC_SCROLL_DOWN_XPOS             SC_SCROLL_UP_XPOS
2563 #define SC_SCROLL_DOWN_YPOS             (SYSIZE - SC_SCROLLBUTTON_YSIZE)
2564 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLL_UP_XPOS
2565 #define SC_SCROLL_VERTICAL_YPOS   (SC_SCROLL_UP_YPOS + SC_SCROLLBUTTON_YSIZE)
2566 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
2567 #define SC_SCROLL_VERTICAL_YSIZE        (SYSIZE - 3 * SC_SCROLLBUTTON_YSIZE)
2568
2569 #define SC_BORDER_SIZE                  14
2570
2571 static struct
2572 {
2573   int xpos, ypos;
2574   int x, y;
2575   int gadget_id;
2576   char *infotext;
2577 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
2578 {
2579   {
2580     SC_SCROLLBUTTON_XPOS + 0 * SC_SCROLLBUTTON_XSIZE,   SC_SCROLLBUTTON_YPOS,
2581     SC_SCROLL_UP_XPOS,                                  SC_SCROLL_UP_YPOS,
2582     SCREEN_CTRL_ID_SCROLL_UP,
2583     "scroll level series up"
2584   },
2585   {
2586     SC_SCROLLBUTTON_XPOS + 1 * SC_SCROLLBUTTON_XSIZE,   SC_SCROLLBUTTON_YPOS,
2587     SC_SCROLL_DOWN_XPOS,                                SC_SCROLL_DOWN_YPOS,
2588     SCREEN_CTRL_ID_SCROLL_DOWN,
2589     "scroll level series down"
2590   }
2591 };
2592
2593 static struct
2594 {
2595   int xpos, ypos;
2596   int x, y;
2597   int width, height;
2598   int type;
2599   int gadget_id;
2600   char *infotext;
2601 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
2602 {
2603   {
2604     SC_SCROLLBAR_XPOS,                  SC_SCROLLBAR_YPOS,
2605     SX + SC_SCROLL_VERTICAL_XPOS,       SY + SC_SCROLL_VERTICAL_YPOS,
2606     SC_SCROLL_VERTICAL_XSIZE,           SC_SCROLL_VERTICAL_YSIZE,
2607     GD_TYPE_SCROLLBAR_VERTICAL,
2608     SCREEN_CTRL_ID_SCROLL_VERTICAL,
2609     "scroll level series vertically"
2610   }
2611 };
2612
2613 static void CreateScreenScrollbuttons()
2614 {
2615   Bitmap *gd_bitmap = pix[PIX_MORE];
2616   struct GadgetInfo *gi;
2617   unsigned long event_mask;
2618   int i;
2619
2620   for (i=0; i<NUM_SCREEN_SCROLLBUTTONS; i++)
2621   {
2622     int id = scrollbutton_info[i].gadget_id;
2623     int x, y, width, height;
2624     int gd_x1, gd_x2, gd_y1, gd_y2;
2625
2626     x = scrollbutton_info[i].x;
2627     y = scrollbutton_info[i].y;
2628
2629     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
2630
2631     x += SX;
2632     y += SY;
2633     width = SC_SCROLLBUTTON_XSIZE;
2634     height = SC_SCROLLBUTTON_YSIZE;
2635     gd_x1 = scrollbutton_info[i].xpos;
2636     gd_y1 = scrollbutton_info[i].ypos;
2637     gd_x2 = gd_x1;
2638     gd_y2 = gd_y1 + SC_SCROLLBUTTON_YSIZE;
2639
2640     gi = CreateGadget(GDI_CUSTOM_ID, id,
2641                       GDI_CUSTOM_TYPE_ID, i,
2642                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
2643                       GDI_X, x,
2644                       GDI_Y, y,
2645                       GDI_WIDTH, width,
2646                       GDI_HEIGHT, height,
2647                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2648                       GDI_STATE, GD_BUTTON_UNPRESSED,
2649                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
2650                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
2651                       GDI_EVENT_MASK, event_mask,
2652                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
2653                       GDI_END);
2654
2655     if (gi == NULL)
2656       Error(ERR_EXIT, "cannot create gadget");
2657
2658     screen_gadget[id] = gi;
2659   }
2660 }
2661
2662 static void CreateScreenScrollbars()
2663 {
2664   int i;
2665
2666   for (i=0; i<NUM_SCREEN_SCROLLBARS; i++)
2667   {
2668     int id = scrollbar_info[i].gadget_id;
2669     Bitmap *gd_bitmap = pix[PIX_MORE];
2670     int gd_x1, gd_x2, gd_y1, gd_y2;
2671     struct GadgetInfo *gi;
2672     int items_max, items_visible, item_position;
2673     unsigned long event_mask;
2674     int num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1;
2675
2676 #if 0
2677     if (num_leveldirs <= MAX_MENU_ENTRIES_ON_SCREEN)
2678       num_page_entries = num_leveldirs;
2679     else
2680       num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1;
2681
2682     items_max = MAX(num_leveldirs, num_page_entries);
2683     items_visible = num_page_entries;
2684     item_position = 0;
2685 #else
2686     items_max = num_page_entries;
2687     items_visible = num_page_entries;
2688     item_position = 0;
2689 #endif
2690
2691     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
2692
2693     gd_x1 = scrollbar_info[i].xpos;
2694     gd_x2 = gd_x1 + scrollbar_info[i].width;
2695     gd_y1 = scrollbar_info[i].ypos;
2696     gd_y2 = scrollbar_info[i].ypos;
2697
2698     gi = CreateGadget(GDI_CUSTOM_ID, id,
2699                       GDI_CUSTOM_TYPE_ID, i,
2700                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
2701                       GDI_X, scrollbar_info[i].x,
2702                       GDI_Y, scrollbar_info[i].y,
2703                       GDI_WIDTH, scrollbar_info[i].width,
2704                       GDI_HEIGHT, scrollbar_info[i].height,
2705                       GDI_TYPE, scrollbar_info[i].type,
2706                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
2707                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
2708                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
2709                       GDI_STATE, GD_BUTTON_UNPRESSED,
2710                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
2711                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
2712                       GDI_BORDER_SIZE, SC_BORDER_SIZE,
2713                       GDI_EVENT_MASK, event_mask,
2714                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
2715                       GDI_END);
2716
2717     if (gi == NULL)
2718       Error(ERR_EXIT, "cannot create gadget");
2719
2720     screen_gadget[id] = gi;
2721   }
2722 }
2723
2724 void CreateScreenGadgets()
2725 {
2726   CreateScreenScrollbuttons();
2727   CreateScreenScrollbars();
2728 }
2729
2730 void MapChooseLevelGadgets()
2731 {
2732   int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
2733   int i;
2734
2735   if (num_leveldirs <= MAX_MENU_ENTRIES_ON_SCREEN)
2736     return;
2737
2738   for (i=0; i<NUM_SCREEN_GADGETS; i++)
2739     MapGadget(screen_gadget[i]);
2740 }
2741
2742 void UnmapChooseLevelGadgets()
2743 {
2744   int i;
2745
2746   for (i=0; i<NUM_SCREEN_GADGETS; i++)
2747     UnmapGadget(screen_gadget[i]);
2748 }
2749
2750 static void HandleScreenGadgets(struct GadgetInfo *gi)
2751 {
2752   int id = gi->custom_id;
2753
2754   if (game_status != CHOOSELEVEL)
2755     return;
2756
2757   switch (id)
2758   {
2759     case SCREEN_CTRL_ID_SCROLL_UP:
2760       HandleChooseLevel(SX,SY + 32, 0,0, MB_MENU_MARK);
2761       break;
2762
2763     case SCREEN_CTRL_ID_SCROLL_DOWN:
2764       HandleChooseLevel(SX,SY + SYSIZE - 32, 0,0, MB_MENU_MARK);
2765       break;
2766
2767     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
2768       HandleChooseLevel(0,0, 999,gi->event.item_position, MB_MENU_INITIALIZE);
2769       break;
2770
2771     default:
2772       break;
2773   }
2774 }