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