rnd-20070208-2-src
[rocksndiamonds.git] / src / screens.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 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 #include "config.h"
27
28 /* screens in the setup menu */
29 #define SETUP_MODE_MAIN                 0
30 #define SETUP_MODE_GAME                 1
31 #define SETUP_MODE_CHOOSE_GAME_SPEED    2
32 #define SETUP_MODE_EDITOR               3
33 #define SETUP_MODE_INPUT                4
34 #define SETUP_MODE_SHORTCUT_1           5
35 #define SETUP_MODE_SHORTCUT_2           6
36 #define SETUP_MODE_GRAPHICS             7
37 #define SETUP_MODE_CHOOSE_SCREEN_MODE   8
38 #define SETUP_MODE_SOUND                9
39 #define SETUP_MODE_ARTWORK              10
40 #define SETUP_MODE_CHOOSE_GRAPHICS      11
41 #define SETUP_MODE_CHOOSE_SOUNDS        12
42 #define SETUP_MODE_CHOOSE_MUSIC         13
43
44 #define MAX_SETUP_MODES                 14
45
46 /* for input setup functions */
47 #define SETUPINPUT_SCREEN_POS_START     0
48 #define SETUPINPUT_SCREEN_POS_END       (SCR_FIELDY - 4)
49 #define SETUPINPUT_SCREEN_POS_EMPTY1    (SETUPINPUT_SCREEN_POS_START + 3)
50 #define SETUPINPUT_SCREEN_POS_EMPTY2    (SETUPINPUT_SCREEN_POS_END - 1)
51
52 /* screens on the info screen */
53 #define INFO_MODE_MAIN                  0
54 #define INFO_MODE_TITLE                 1
55 #define INFO_MODE_ELEMENTS              2
56 #define INFO_MODE_MUSIC                 3
57 #define INFO_MODE_CREDITS               4
58 #define INFO_MODE_PROGRAM               5
59 #define INFO_MODE_VERSION               6
60 #define INFO_MODE_LEVELSET              7
61
62 #define MAX_INFO_MODES                  8
63
64 /* for various menu stuff  */
65 #define MENU_SCREEN_START_XPOS          1
66 #define MENU_SCREEN_START_YPOS          2
67 #define MENU_SCREEN_VALUE_XPOS          14
68 #define MENU_SCREEN_MAX_XPOS            (SCR_FIELDX - 1)
69 #define MENU_TITLE1_YPOS                8
70 #define MENU_TITLE2_YPOS                46
71 #define MAX_INFO_ELEMENTS_ON_SCREEN     10
72 #define MAX_MENU_ENTRIES_ON_SCREEN      (SCR_FIELDY - MENU_SCREEN_START_YPOS)
73 #define MAX_MENU_TEXT_LENGTH_BIG        (MENU_SCREEN_VALUE_XPOS -       \
74                                          MENU_SCREEN_START_XPOS)
75 #define MAX_MENU_TEXT_LENGTH_MEDIUM     (MAX_MENU_TEXT_LENGTH_BIG * 2)
76
77 /* buttons and scrollbars identifiers */
78 #define SCREEN_CTRL_ID_PREV_LEVEL       0
79 #define SCREEN_CTRL_ID_NEXT_LEVEL       1
80 #define SCREEN_CTRL_ID_PREV_PLAYER      2
81 #define SCREEN_CTRL_ID_NEXT_PLAYER      3
82 #define SCREEN_CTRL_ID_SCROLL_UP        4
83 #define SCREEN_CTRL_ID_SCROLL_DOWN      5
84 #define SCREEN_CTRL_ID_SCROLL_VERTICAL  6
85
86 #define NUM_SCREEN_GADGETS              7
87
88 #define NUM_SCREEN_MENUBUTTONS          4
89 #define NUM_SCREEN_SCROLLBUTTONS        2
90 #define NUM_SCREEN_SCROLLBARS           1
91
92 #define SCREEN_MASK_MAIN                (1 << 0)
93 #define SCREEN_MASK_INPUT               (1 << 1)
94
95 /* graphic position and size values for buttons and scrollbars */
96 #define SC_MENUBUTTON_XSIZE             TILEX
97 #define SC_MENUBUTTON_YSIZE             TILEY
98
99 #define SC_SCROLLBUTTON_XSIZE           TILEX
100 #define SC_SCROLLBUTTON_YSIZE           TILEY
101
102 #define SC_SCROLLBAR_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
103
104 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
105 #define SC_SCROLL_VERTICAL_YSIZE        ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \
106                                          SC_SCROLLBUTTON_YSIZE)
107
108 #define SC_SCROLL_UP_XPOS               SC_SCROLLBAR_XPOS
109 #define SC_SCROLL_UP_YPOS               (2 * SC_SCROLLBUTTON_YSIZE)
110
111 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLLBAR_XPOS
112 #define SC_SCROLL_VERTICAL_YPOS         (SC_SCROLL_UP_YPOS + \
113                                          SC_SCROLLBUTTON_YSIZE)
114
115 #define SC_SCROLL_DOWN_XPOS             SC_SCROLLBAR_XPOS
116 #define SC_SCROLL_DOWN_YPOS             (SC_SCROLL_VERTICAL_YPOS + \
117                                          SC_SCROLL_VERTICAL_YSIZE)
118
119 #define SC_BORDER_SIZE                  14
120
121 /* other useful macro definitions */
122 #define BUTTON_GRAPHIC_ACTIVE(g)                                               \
123         (g == IMG_MENU_BUTTON_LEFT       ? IMG_MENU_BUTTON_LEFT_ACTIVE       : \
124          g == IMG_MENU_BUTTON_RIGHT      ? IMG_MENU_BUTTON_RIGHT_ACTIVE      : \
125          g == IMG_MENU_BUTTON_UP         ? IMG_MENU_BUTTON_UP_ACTIVE         : \
126          g == IMG_MENU_BUTTON_DOWN       ? IMG_MENU_BUTTON_DOWN_ACTIVE       : \
127          g == IMG_MENU_BUTTON_LEAVE_MENU ? IMG_MENU_BUTTON_LEAVE_MENU_ACTIVE : \
128          g == IMG_MENU_BUTTON_ENTER_MENU ? IMG_MENU_BUTTON_ENTER_MENU_ACTIVE : \
129          g == IMG_MENU_BUTTON_PREV_LEVEL ? IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE : \
130          g == IMG_MENU_BUTTON_NEXT_LEVEL ? IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE : \
131          IMG_MENU_BUTTON_ACTIVE)
132
133
134 /* forward declarations of internal functions */
135 static void HandleScreenGadgets(struct GadgetInfo *);
136 static void HandleSetupScreen_Generic(int, int, int, int, int);
137 static void HandleSetupScreen_Input(int, int, int, int, int);
138 static void CustomizeKeyboard(int);
139 static void CalibrateJoystick(int);
140 static void execSetupGame(void);
141 static void execSetupGraphics(void);
142 static void execSetupArtwork(void);
143 static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
144
145 static void DrawChooseLevel(void);
146 static void DrawInfoScreen(void);
147 static void DrawAndFadeInInfoScreen(int);
148 static void DrawSetupScreen(void);
149
150 static void DrawInfoScreenExt(int, int);
151 static void DrawInfoScreen_NotAvailable(char *, char *);
152 static void DrawInfoScreen_HelpAnim(int, int, boolean);
153 static void DrawInfoScreen_HelpText(int, int, int, int);
154 static void HandleInfoScreen_Main(int, int, int, int, int);
155 static void HandleInfoScreen_TitleScreen(int);
156 static void HandleInfoScreen_Elements(int);
157 static void HandleInfoScreen_Music(int);
158 static void HandleInfoScreen_Credits(int);
159 static void HandleInfoScreen_Program(int);
160 static void HandleInfoScreen_Version(int);
161
162 static void MapScreenMenuGadgets(int);
163 static void MapScreenTreeGadgets(TreeInfo *);
164
165 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
166
167 static int setup_mode = SETUP_MODE_MAIN;
168 static int info_mode = INFO_MODE_MAIN;
169
170 static TreeInfo *screen_modes = NULL;
171 static TreeInfo *screen_mode_current = NULL;
172
173 static TreeInfo *game_speeds = NULL;
174 static TreeInfo *game_speed_current = NULL;
175
176 static struct
177 {
178   int value;
179   char *text;
180 } game_speeds_list[] =
181 {
182 #if 1
183   {     30,     "Very Slow"                     },
184   {     25,     "Slow"                          },
185   {     20,     "Normal"                        },
186   {     15,     "Fast"                          },
187   {     10,     "Very Fast"                     },
188 #else
189   {     1000,   "1/1s (Extremely Slow)"         },
190   {     500,    "1/2s"                          },
191   {     200,    "1/5s"                          },
192   {     100,    "1/10s"                         },
193   {     50,     "1/20s"                         },
194   {     29,     "1/35s (Original Supaplex)"     },
195   {     25,     "1/40s"                         },
196   {     20,     "1/50s (Normal Speed)"          },
197   {     14,     "1/70s (Maximum Supaplex)"      },
198   {     10,     "1/100s"                        },
199   {     5,      "1/200s"                        },
200   {     2,      "1/500s"                        },
201   {     1,      "1/1000s (Extremely Fast)"      },
202 #endif
203
204   {     -1,     NULL                            },
205 };
206
207 #define DRAW_MODE(s)            ((s) >= GAME_MODE_MAIN &&               \
208                                  (s) <= GAME_MODE_SETUP ? (s) :         \
209                                  (s) == GAME_MODE_PSEUDO_TYPENAME ?     \
210                                  GAME_MODE_MAIN : GAME_MODE_DEFAULT)
211
212 #define DRAW_MODE_INFO(i)       ((i) >= INFO_MODE_ELEMENTS &&           \
213                                  (i) <= INFO_MODE_LEVELSET ? (i) :      \
214                                  INFO_MODE_MAIN)
215
216 #define DRAW_XOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?  \
217                                  menu.draw_xoffset[GAME_MODE_INFO] :    \
218                                  menu.draw_xoffset_info[DRAW_MODE_INFO(i)])
219 #define DRAW_YOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?  \
220                                  menu.draw_yoffset[GAME_MODE_INFO] :    \
221                                  menu.draw_yoffset_info[DRAW_MODE_INFO(i)])
222
223 #define DRAW_XOFFSET(s)         ((s) == GAME_MODE_INFO ?                \
224                                  DRAW_XOFFSET_INFO(info_mode) :         \
225                                  menu.draw_xoffset[DRAW_MODE(s)])
226 #define DRAW_YOFFSET(s)         ((s) == GAME_MODE_INFO ?                \
227                                  DRAW_YOFFSET_INFO(info_mode) :         \
228                                  menu.draw_yoffset[DRAW_MODE(s)])
229
230 #define mSX                     (SX + DRAW_XOFFSET(game_status))
231 #define mSY                     (SY + DRAW_YOFFSET(game_status))
232
233 #define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ?   \
234                                     menu.list_size[game_status] :       \
235                                     MAX_MENU_ENTRIES_ON_SCREEN)
236
237 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
238 #define NUM_SCROLLBAR_BITMAPS           2
239 static Bitmap *scrollbar_bitmap[NUM_SCROLLBAR_BITMAPS];
240 #endif
241
242
243 /* title display and control definitions */
244
245 #define MAX_NUM_TITLE_SCREENS   (2 * MAX_NUM_TITLE_IMAGES +             \
246                                  2 * MAX_NUM_TITLE_MESSAGES)
247
248 static boolean show_title_initial = TRUE;
249 static int num_title_screens = 0;
250
251 struct TitleControlInfo
252 {
253   boolean is_image;
254   boolean initial;
255   int local_nr;
256 };
257
258 struct TitleControlInfo title_controls[MAX_NUM_TITLE_SCREENS];
259
260
261 /* main menu display and control definitions */
262
263 #define MAIN_CONTROL_NAME               0
264 #define MAIN_CONTROL_LEVELS             1
265 #define MAIN_CONTROL_SCORES             2
266 #define MAIN_CONTROL_EDITOR             3
267 #define MAIN_CONTROL_INFO               4
268 #define MAIN_CONTROL_GAME               5
269 #define MAIN_CONTROL_SETUP              6
270 #define MAIN_CONTROL_QUIT               7
271 #define MAIN_CONTROL_PREV_LEVEL         8
272 #define MAIN_CONTROL_NEXT_LEVEL         9
273 #define MAIN_CONTROL_CURRENT_LEVEL      10
274 #define MAIN_CONTROL_FIRST_LEVEL        11
275 #define MAIN_CONTROL_LAST_LEVEL         12
276 #define MAIN_CONTROL_LEVEL_INFO_1       13
277 #define MAIN_CONTROL_LEVEL_INFO_2       14
278 #define MAIN_CONTROL_TITLE_1            15
279 #define MAIN_CONTROL_TITLE_2            16
280 #define MAIN_CONTROL_TITLE_3            17
281
282 static char main_text_name[10];
283 static char main_text_current_level[10];
284 static char main_text_first_level[10];
285 static char main_text_last_level[10];
286 static char main_input_name[MAX_PLAYER_NAME_LEN + 1];
287
288 struct MainControlInfo
289 {
290   int nr;
291
292   struct MenuPosInfo *pos_button;
293   int button_graphic;
294
295   struct TextPosInfo *pos_text;
296   char *text;
297   int font_text;
298
299   struct MenuPosInfo *pos_input;
300   char *input;
301   int font_input;
302 };
303
304 static struct MainControlInfo main_controls[] =
305 {
306   {
307     MAIN_CONTROL_NAME,
308     &menu.main.button.name,             IMG_MENU_BUTTON,
309     &menu.main.text.name,               main_text_name,         FONT_MENU_1,
310     &menu.main.input.name,              main_input_name,        FONT_INPUT_1,
311   },
312   {
313     MAIN_CONTROL_LEVELS,
314     &menu.main.button.levels,           IMG_MENU_BUTTON_ENTER_MENU,
315     &menu.main.text.levels,             "Levelset",             FONT_MENU_1,
316     NULL,                               NULL,                   -1,
317   },
318   {
319     MAIN_CONTROL_SCORES,
320     &menu.main.button.scores,           IMG_MENU_BUTTON,
321     &menu.main.text.scores,             "Hall Of Fame",         FONT_MENU_1,
322     NULL,                               NULL,                   -1,
323   },
324   {
325     MAIN_CONTROL_EDITOR,
326     &menu.main.button.editor,           IMG_MENU_BUTTON,
327     &menu.main.text.editor,             "Level Creator",        FONT_MENU_1,    
328     NULL,                               NULL,                   -1,
329   },
330   {
331     MAIN_CONTROL_INFO,
332     &menu.main.button.info,             IMG_MENU_BUTTON_ENTER_MENU,
333     &menu.main.text.info,               "Info Screen",          FONT_MENU_1,
334     NULL,                               NULL,                   -1,
335   },
336   {
337     MAIN_CONTROL_GAME,
338     &menu.main.button.game,             IMG_MENU_BUTTON,
339     &menu.main.text.game,               "Start Game",           FONT_MENU_1,
340     NULL,                               NULL,                   -1,
341   },
342   {
343     MAIN_CONTROL_SETUP,
344     &menu.main.button.setup,            IMG_MENU_BUTTON_ENTER_MENU,
345     &menu.main.text.setup,              "Setup",                FONT_MENU_1,
346     NULL,                               NULL,                   -1,
347   },
348   {
349     MAIN_CONTROL_QUIT,
350     &menu.main.button.quit,             IMG_MENU_BUTTON,
351     &menu.main.text.quit,               "Quit",                 FONT_MENU_1,
352     NULL,                               NULL,                   -1,
353   },
354 #if 0
355   /* (these two buttons are real gadgets) */
356   {
357     MAIN_CONTROL_PREV_LEVEL,
358     &menu.main.button.prev_level,       IMG_MENU_BUTTON_PREV_LEVEL,
359     NULL,                               NULL,                   -1,
360     NULL,                               NULL,                   -1,
361   },
362   {
363     MAIN_CONTROL_NEXT_LEVEL,
364     &menu.main.button.next_level,       IMG_MENU_BUTTON_NEXT_LEVEL,
365     NULL,                               NULL,                   -1,
366     NULL,                               NULL,                   -1,
367   },
368 #endif
369   {
370     MAIN_CONTROL_CURRENT_LEVEL,
371     NULL,                               -1,
372     &menu.main.text.current_level,      main_text_current_level,FONT_VALUE_1,
373     NULL,                               NULL,                   -1,
374   },
375   {
376     MAIN_CONTROL_FIRST_LEVEL,
377     NULL,                               -1,
378     &menu.main.text.first_level,        main_text_first_level,  FONT_TEXT_3,
379     NULL,                               NULL,                   -1,
380   },
381   {
382     MAIN_CONTROL_LAST_LEVEL,
383     NULL,                               -1,
384     &menu.main.text.last_level,         main_text_last_level,   FONT_TEXT_3,
385     NULL,                               NULL,                   -1,
386   },
387   {
388     MAIN_CONTROL_LEVEL_INFO_1,
389     NULL,                               -1,
390     &menu.main.text.level_info_1,       NULL,                   -1,
391     NULL,                               NULL,                   -1,
392   },
393   {
394     MAIN_CONTROL_LEVEL_INFO_2,
395     NULL,                               -1,
396     &menu.main.text.level_info_2,       NULL,                   -1,
397     NULL,                               NULL,                   -1,
398   },
399   {
400     MAIN_CONTROL_TITLE_1,
401     NULL,                               -1,
402     &menu.main.text.title_1,            PROGRAM_TITLE_STRING,   FONT_TITLE_1,
403     NULL,                               NULL,                   -1,
404   },
405   {
406     MAIN_CONTROL_TITLE_2,
407     NULL,                               -1,
408     &menu.main.text.title_2,            PROGRAM_COPYRIGHT_STRING, FONT_TITLE_2,
409     NULL,                               NULL,                   -1,
410   },
411   {
412     MAIN_CONTROL_TITLE_3,
413     NULL,                               -1,
414     &menu.main.text.title_3,            PROGRAM_GAME_BY_STRING, FONT_TITLE_2,
415     NULL,                               NULL,                   -1,
416   },
417
418   {
419     -1,
420     NULL,                               -1,
421     NULL,                               NULL,                   -1,
422     NULL,                               NULL,                   -1,
423   }
424 };
425
426
427 static int getTitleScreenGraphic(int nr, boolean initial)
428 {
429   return (initial ? IMG_TITLESCREEN_INITIAL_1 : IMG_TITLESCREEN_1) + nr;
430 }
431
432 static void InitializeTitleControlsExt_AddTitleInfo(boolean is_image,
433                                                     boolean initial, int nr)
434 {
435   title_controls[num_title_screens].is_image = is_image;
436   title_controls[num_title_screens].initial = initial;
437   title_controls[num_title_screens].local_nr = nr;
438
439   num_title_screens++;
440 }
441
442 static void InitializeTitleControls_CheckTitleInfo(boolean initial)
443 {
444   int i;
445
446   for (i = 0; i < MAX_NUM_TITLE_IMAGES; i++)
447     if (graphic_info[getTitleScreenGraphic(i, initial)].bitmap != NULL)
448       InitializeTitleControlsExt_AddTitleInfo(TRUE, initial, i);
449
450   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
451     if (getLevelSetTitleMessageFilename(i, initial) != NULL)
452       InitializeTitleControlsExt_AddTitleInfo(FALSE, initial, i);
453 }
454
455 static void InitializeTitleControls()
456 {
457   num_title_screens = 0;
458
459   if (show_title_initial)
460     InitializeTitleControls_CheckTitleInfo(TRUE);
461
462   InitializeTitleControls_CheckTitleInfo(FALSE);
463 }
464
465 static void InitializeMainControls()
466 {
467   boolean local_team_mode = (!options.network && setup.team_mode);
468   int i;
469
470   /* set main control text values to dynamically determined values */
471   sprintf(main_text_name,          "%s",   local_team_mode ? "Team:" : "Name:");
472   sprintf(main_text_current_level, "%s",   int2str(level_nr, 3));
473   sprintf(main_text_first_level,   "%03d", leveldir_current->first_level);
474   sprintf(main_text_last_level,    "%03d", leveldir_current->last_level);
475   sprintf(main_input_name,         "%s",   setup.player_name);
476
477   /* set main control screen positions to dynamically determined values */
478   for (i = 0; main_controls[i].nr != -1; i++)
479   {
480     struct MainControlInfo *mci = &main_controls[i];
481     int nr                         = mci->nr;
482     struct MenuPosInfo *pos_button = mci->pos_button;
483     struct TextPosInfo *pos_text   = mci->pos_text;
484     struct MenuPosInfo *pos_input  = mci->pos_input;
485     char *text                     = mci->text;
486     char *input                    = mci->input;
487     int button_graphic             = mci->button_graphic;
488     int font_text                  = mci->font_text;
489     int font_input                 = mci->font_input;
490
491     int font_text_width   = (font_text  != -1 ? getFontWidth(font_text)   : 0);
492     int font_text_height  = (font_text  != -1 ? getFontHeight(font_text)  : 0);
493     int font_input_width  = (font_input != -1 ? getFontWidth(font_input)  : 0);
494     int font_input_height = (font_input != -1 ? getFontHeight(font_input) : 0);
495     int text_chars  = (text  != NULL ? strlen(text)  : 0);
496     int input_chars = (input != NULL ? strlen(input) : 0);
497
498     int button_width =
499       (button_graphic != -1 ? graphic_info[button_graphic].width  : 0);
500     int button_height =
501       (button_graphic != -1 ? graphic_info[button_graphic].height : 0);
502     int text_width   = font_text_width * text_chars;
503     int text_height  = font_text_height;
504     int input_width  = font_input_width * input_chars;
505     int input_height = font_input_height;
506
507     if (nr == MAIN_CONTROL_NAME)
508     {
509 #if 0
510       if (menu.main.input.name.x == -1)
511         menu.main.input.name.x = menu.main.text.name.x + text_width;
512       if (menu.main.input.name.y == -1)
513         menu.main.input.name.y = menu.main.text.name.y;
514 #endif
515
516 #if 1
517       menu.main.input.name.width  = input_width;
518       menu.main.input.name.height = input_height;
519 #else
520       menu.main.input.name.width  = font_input_width * MAX_PLAYER_NAME_LEN;
521       menu.main.input.name.height = font_input_height;
522 #endif
523     }
524
525     if (pos_button != NULL)
526     {
527       if (pos_button->width == 0)
528         pos_button->width = button_width;
529       if (pos_button->height == 0)
530         pos_button->height = button_height;
531     }
532
533     if (pos_text != NULL)
534     {
535       /* calculate width for non-clickable text -- needed for text alignment */
536       boolean calculate_text_width = (pos_button == NULL && text != NULL);
537
538       if (pos_text->x == -1 && pos_button != NULL)
539         pos_text->x = pos_button->x + pos_button->width;
540       if (pos_text->y == -1 && pos_button != NULL)
541         pos_text->y = pos_button->y;
542
543       if (pos_text->width == -1 || calculate_text_width)
544         pos_text->width = text_width;
545       if (pos_text->height == -1)
546         pos_text->height = text_height;
547     }
548
549     if (pos_input != NULL)
550     {
551       if (pos_input->x == -1 && pos_text != NULL)
552         pos_input->x = pos_text->x + pos_text->width;
553       if (pos_input->y == -1 && pos_text != NULL)
554         pos_input->y = pos_text->y;
555
556       if (pos_input->width == -1)
557         pos_input->width = input_width;
558       if (pos_input->height == -1)
559         pos_input->height = input_height;
560     }
561   }
562 }
563
564 static void DrawCursorAndText_Main_Ext(int nr, boolean active_text,
565                                        boolean active_input)
566 {
567   int i;
568
569   for (i = 0; main_controls[i].nr != -1; i++)
570   {
571     struct MainControlInfo *mci = &main_controls[i];
572
573     if (mci->nr == nr || nr == -1)
574     {
575       struct MenuPosInfo *pos_button = mci->pos_button;
576       struct TextPosInfo *pos_text   = mci->pos_text;
577       struct MenuPosInfo *pos_input  = mci->pos_input;
578       char *text                     = mci->text;
579       char *input                    = mci->input;
580       int button_graphic             = mci->button_graphic;
581       int font_text                  = mci->font_text;
582       int font_input                 = mci->font_input;
583
584       if (active_text)
585       {
586         button_graphic = BUTTON_GRAPHIC_ACTIVE(button_graphic);
587         font_text = FONT_ACTIVE(font_text);
588       }
589
590       if (active_input)
591       {
592         font_input = FONT_ACTIVE(font_input);
593       }
594
595       if (pos_button != NULL)
596       {
597         struct MenuPosInfo *pos = pos_button;
598         int x = mSX + pos->x;
599         int y = mSY + pos->y;
600
601         DrawBackgroundForGraphic(x, y, pos->width, pos->height, button_graphic);
602         DrawGraphicThruMaskExt(drawto, x, y, button_graphic, 0);
603       }
604
605       if (pos_text != NULL && text != NULL)
606       {
607         struct TextPosInfo *pos = pos_text;
608         int x = mSX + ALIGNED_MENU_XPOS(pos);
609         int y = mSY + ALIGNED_MENU_YPOS(pos);
610
611         DrawBackgroundForFont(x, y, pos->width, pos->height, font_text);
612         DrawText(x, y, text, font_text);
613       }
614
615       if (pos_input != NULL && input != NULL)
616       {
617         struct MenuPosInfo *pos = pos_input;
618         int x = mSX + ALIGNED_MENU_XPOS(pos);
619         int y = mSY + ALIGNED_MENU_YPOS(pos);
620
621         DrawBackgroundForFont(x, y, pos->width, pos->height, font_input);
622         DrawText(x, y, input, font_input);
623       }
624     }
625   }
626 }
627
628 static void DrawCursorAndText_Main(int nr, boolean active_text)
629 {
630   DrawCursorAndText_Main_Ext(nr, active_text, FALSE);
631 }
632
633 #if 0
634 static void DrawCursorAndText_Main_Input(int nr, boolean active_text)
635 {
636   DrawCursorAndText_Main_Ext(nr, active_text, TRUE);
637 }
638 #endif
639
640 static struct MainControlInfo *getMainControlInfo(int nr)
641 {
642   int i;
643
644   for (i = 0; main_controls[i].nr != -1; i++)
645     if (main_controls[i].nr == nr)
646       return &main_controls[i];
647
648   return NULL;
649 }
650
651 static boolean insideMenuPosRect(struct MenuPosInfo *rect, int x, int y)
652 {
653   if (rect == NULL)
654     return FALSE;
655
656   int rect_x = ALIGNED_MENU_XPOS(rect);
657   int rect_y = ALIGNED_MENU_YPOS(rect);
658
659   return (x >= rect_x && x < rect_x + rect->width &&
660           y >= rect_y && y < rect_y + rect->height);
661 }
662
663 static boolean insideTextPosRect(struct TextPosInfo *rect, int x, int y)
664 {
665   if (rect == NULL)
666     return FALSE;
667
668   int rect_x = ALIGNED_MENU_XPOS(rect);
669   int rect_y = ALIGNED_MENU_YPOS(rect);
670
671   return (x >= rect_x && x < rect_x + rect->width &&
672           y >= rect_y && y < rect_y + rect->height);
673 }
674
675 static void drawCursorExt(int xpos, int ypos, boolean active, int graphic)
676 {
677   static int cursor_array[SCR_FIELDY];
678   int x = mSX + TILEX * xpos;
679   int y = mSY + TILEY * (MENU_SCREEN_START_YPOS + ypos);
680
681   if (xpos == 0)
682   {
683     if (graphic != -1)
684       cursor_array[ypos] = graphic;
685     else
686       graphic = cursor_array[ypos];
687   }
688
689   if (active)
690     graphic = BUTTON_GRAPHIC_ACTIVE(graphic);
691
692   DrawBackgroundForGraphic(x, y, TILEX, TILEY, graphic);
693   DrawGraphicThruMaskExt(drawto, x, y, graphic, 0);
694 }
695
696 static void initCursor(int ypos, int graphic)
697 {
698   drawCursorExt(0, ypos, FALSE, graphic);
699 }
700
701 static void drawCursor(int ypos, boolean active)
702 {
703   drawCursorExt(0, ypos, active, -1);
704 }
705
706 static void drawCursorXY(int xpos, int ypos, int graphic)
707 {
708   drawCursorExt(xpos, ypos, FALSE, graphic);
709 }
710
711 static void drawChooseTreeCursor(int ypos, boolean active)
712 {
713   int last_game_status = game_status;   /* save current game status */
714
715   /* force LEVELS draw offset on artwork setup screen */
716   game_status = GAME_MODE_LEVELS;
717
718   drawCursorExt(0, ypos, active, -1);
719
720   game_status = last_game_status;       /* restore current game status */
721 }
722
723 void DrawHeadline()
724 {
725   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, PROGRAM_TITLE_STRING);
726   DrawTextSCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, PROGRAM_COPYRIGHT_STRING);
727 }
728
729 #if 0
730 static int getPrevlevelButtonPos()
731 {
732   return 10;
733 }
734
735 static int getCurrentLevelTextPos()
736 {
737   return (getPrevlevelButtonPos() + 1);
738 }
739
740 static int getNextLevelButtonPos()
741 {
742   return getPrevlevelButtonPos() + 3 + 1;
743 }
744
745 static int getLevelRangeTextPos()
746 {
747   return getNextLevelButtonPos() + 1;
748 }
749 #endif
750
751 int effectiveGameStatus()
752 {
753   if (game_status == GAME_MODE_INFO && info_mode == INFO_MODE_TITLE)
754     return GAME_MODE_TITLE;
755
756   return game_status;
757 }
758
759 void DrawTitleScreenImage(int nr, boolean initial)
760 {
761   int graphic = getTitleScreenGraphic(nr, initial);
762   Bitmap *bitmap = graphic_info[graphic].bitmap;
763 #if 1
764   int width  = graphic_info[graphic].width;
765   int height = graphic_info[graphic].height;
766   int src_x = graphic_info[graphic].src_x;
767   int src_y = graphic_info[graphic].src_y;
768 #else
769   int width  = graphic_info[graphic].src_image_width;
770   int height = graphic_info[graphic].src_image_height;
771   int src_x = 0, src_y = 0;
772 #endif
773   int dst_x, dst_y;
774
775   if (bitmap == NULL)
776     return;
777
778   if (width > WIN_XSIZE)
779   {
780     /* image width too large for window => center image horizontally */
781     src_x = (width - WIN_XSIZE) / 2;
782     width = WIN_XSIZE;
783   }
784
785   if (height > WIN_YSIZE)
786   {
787     /* image height too large for window => center image vertically */
788     src_y = (height - WIN_YSIZE) / 2;
789     height = WIN_YSIZE;
790   }
791
792   dst_x = (WIN_XSIZE - width) / 2;
793   dst_y = (WIN_YSIZE - height) / 2;
794
795   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
796
797   if (DrawingOnBackground(dst_x, dst_y))
798     BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
799   else
800     BlitBitmap(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
801
802   redraw_mask = REDRAW_ALL;
803
804   /* reset fading control values to default config settings */
805   title.fade_delay_final = title.fade_delay;
806   title.post_delay_final = title.post_delay;
807   title.auto_delay_final = title.auto_delay;
808
809   /* override default settings with image config settings, if defined */
810   if (graphic_info[graphic].fade_delay > -1)
811     title.fade_delay_final = graphic_info[graphic].fade_delay;
812   if (graphic_info[graphic].post_delay > -1)
813     title.post_delay_final = graphic_info[graphic].post_delay;
814   if (graphic_info[graphic].auto_delay > -1)
815     title.auto_delay_final = graphic_info[graphic].auto_delay;
816 }
817
818 void DrawTitleScreenMessage(int nr, boolean initial)
819 {
820   char *filename = getLevelSetTitleMessageFilename(nr, initial);
821   int font_nr = FONT_TEXT_1;
822   int font_width;
823   int font_height;
824   int pad_x = 16   + 4;
825   int pad_y = 32   + 14;
826   int sx = pad_x;
827   int sy = pad_y;
828   int max_chars_per_line;
829   int max_lines_per_screen;
830   int last_game_status = game_status;   /* save current game status */
831
832   if (filename == NULL)
833     return;
834
835   SetDrawBackgroundMask(REDRAW_ALL);
836   SetWindowBackgroundImageIfDefined(IMG_BACKGROUND_MESSAGE);
837
838   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
839
840   /* force MESSAGE font on title message screen */
841   game_status = GAME_MODE_MESSAGE;
842
843   font_width = getFontWidth(font_nr);
844   font_height = getFontHeight(font_nr);
845   max_chars_per_line = (WIN_XSIZE - 2 * pad_x) / font_width;
846   max_lines_per_screen = (WIN_YSIZE - pad_y) / font_height - 1;
847
848   DrawTextFromFile(sx, sy, filename, font_nr, max_chars_per_line,
849                    max_lines_per_screen, FALSE);
850
851   game_status = last_game_status;       /* restore current game status */
852 }
853
854 void DrawTitleScreen()
855 {
856   KeyboardAutoRepeatOff();
857
858   SetMainBackgroundImage(IMG_BACKGROUND_TITLE);
859
860   HandleTitleScreen(0, 0, 0, 0, MB_MENU_INITIALIZE);
861
862   StopAnimation();
863 }
864
865 void DrawMainMenuExt(int redraw_mask, boolean do_fading)
866 {
867   static LevelDirTree *leveldir_last_valid = NULL;
868   boolean levelset_has_changed = FALSE;
869 #if 0
870   boolean local_team_mode = (!options.network && setup.team_mode);
871   char *name_text = (local_team_mode ? "Team:" : "Name:");
872   int name_width, level_width;
873 #endif
874 #if 0
875   int i;
876 #endif
877
878   UnmapAllGadgets();
879   FadeSoundsAndMusic();
880
881   KeyboardAutoRepeatOn();
882   ActivateJoystick();
883
884   SetDrawDeactivationMask(REDRAW_NONE);
885   SetDrawBackgroundMask(REDRAW_FIELD);
886
887   audio.sound_deactivated = FALSE;
888
889   GetPlayerConfig();
890
891   /* needed if last screen was the playing screen, invoked from level editor */
892   if (level_editor_test_game)
893   {
894     game_status = GAME_MODE_EDITOR;
895     DrawLevelEd();
896
897     return;
898   }
899
900   /* needed if last screen was the editor screen */
901   UndrawSpecialEditorDoor();
902
903   /* needed if last screen was the setup screen and fullscreen state changed */
904   ToggleFullscreenIfNeeded();
905
906   /* leveldir_current may be invalid (level group, parent link) */
907   if (!validLevelSeries(leveldir_current))
908     leveldir_current = getFirstValidTreeInfoEntry(leveldir_last_valid);
909
910   if (leveldir_current != leveldir_last_valid)
911     levelset_has_changed = TRUE;
912
913   /* store valid level series information */
914   leveldir_last_valid = leveldir_current;
915
916   /* needed if last screen (level choice) changed graphics, sounds or music */
917   ReloadCustomArtwork(0);
918
919 #if defined(TARGET_SDL)
920   SetDrawtoField(DRAW_BACKBUFFER);
921 #endif
922
923 #if 1
924   if (setup.show_titlescreen && (show_title_initial || levelset_has_changed))
925   {
926     /* needed to be able to skip title screen, if no image or message defined */
927     InitializeTitleControls();
928
929     if (num_title_screens > 0)
930     {
931       game_status = GAME_MODE_TITLE;
932
933       DrawTitleScreen();
934
935       return;
936     }
937   }
938 #else
939   if (setup.show_titlescreen &&
940       ((levelset_has_changed &&
941         (graphic_info[IMG_TITLESCREEN_1].bitmap != NULL ||
942          getLevelSetMessageFilename(1, FALSE) != NULL)) ||
943        (show_title_initial &&
944         (graphic_info[IMG_TITLESCREEN_INITIAL_1].bitmap != NULL ||
945          getLevelSetMessageFilename(1, TRUE) != NULL))))
946   {
947     game_status = GAME_MODE_TITLE;
948
949     DrawTitleScreen();
950
951     return;
952   }
953 #endif
954
955   /* level_nr may have been set to value over handicap with level editor */
956   if (setup.handicap && level_nr > leveldir_current->handicap_level)
957     level_nr = leveldir_current->handicap_level;
958
959   LoadLevel(level_nr);
960
961   SetMainBackgroundImage(IMG_BACKGROUND_MAIN);
962   ClearWindow();
963
964 #if 1
965   InitializeMainControls();
966
967 #if 1
968   DrawCursorAndText_Main(-1, FALSE);
969 #else
970   for (i = 0; main_controls[i].nr != -1; i++)
971   {
972     struct MenuPosInfo *pos_button = main_controls[i].pos_button;
973     struct MenuPosInfo *pos_text   = main_controls[i].pos_text;
974     struct MenuPosInfo *pos_input  = main_controls[i].pos_input;
975     char *text                     = main_controls[i].text;
976     char *input                    = main_controls[i].input;
977     int button_graphic             = main_controls[i].button_graphic;
978     int font_text                  = main_controls[i].font_text;
979     int font_input                 = main_controls[i].font_input;
980
981     if (pos_button != NULL)
982       DrawGraphicThruMaskExt(drawto, mSX + pos_button->x, mSY + pos_button->y,
983                              button_graphic, 0);
984
985     if (pos_text != NULL && text != NULL)
986       DrawText(mSX + pos_text->x, mSY + pos_text->y, text, font_text);
987
988     if (pos_input != NULL && input != NULL)
989       DrawText(mSX + pos_input->x, mSY + pos_input->y, input, font_input);
990   }
991 #endif
992
993 #else
994
995   DrawHeadline();
996
997   DrawText(mSX + 32, mSY + 2 * 32, name_text,       FONT_MENU_1);
998   DrawText(mSX + 32, mSY + 3 * 32, "Levelset",      FONT_MENU_1);
999   DrawText(mSX + 32, mSY + 4 * 32, "Hall Of Fame",  FONT_MENU_1);
1000   DrawText(mSX + 32, mSY + 5 * 32, "Level Creator", FONT_MENU_1);
1001   DrawText(mSX + 32, mSY + 6 * 32, "Info Screen",   FONT_MENU_1);
1002   DrawText(mSX + 32, mSY + 7 * 32, "Start Game",    FONT_MENU_1);
1003   DrawText(mSX + 32, mSY + 8 * 32, "Setup",         FONT_MENU_1);
1004   DrawText(mSX + 32, mSY + 9 * 32, "Quit",          FONT_MENU_1);
1005
1006   /* calculated after (possible) reload of custom artwork */
1007   name_width  = getTextWidth(name_text,  FONT_MENU_1);
1008   level_width = 9 * 32;
1009
1010   DrawText(mSX + 32 + name_width, mSY + 2 * 32, setup.player_name,
1011            FONT_INPUT_1);
1012
1013   DrawText(mSX + getCurrentLevelTextPos() * 32, mSY + 3 * 32,
1014            int2str(level_nr, 3), FONT_VALUE_1);
1015
1016   {
1017     int text_height = getFontHeight(FONT_TEXT_3);
1018     int xpos = getLevelRangeTextPos() * 32 + 8;
1019     int ypos2 = 3 * 32 + 16;
1020     int ypos1 = ypos2 - text_height;
1021
1022     DrawTextF(mSX - SX + xpos, mSY - SY + ypos1, FONT_TEXT_3,
1023               "%03d", leveldir_current->first_level);
1024     DrawTextF(mSX - SX + xpos, mSY - SY + ypos2, FONT_TEXT_3,
1025               "%03d", leveldir_current->last_level);
1026   }
1027
1028   for (i = 0; i < 8; i++)
1029     initCursor(i, (i == 1 || i == 4 || i == 6 ? IMG_MENU_BUTTON_ENTER_MENU :
1030                    IMG_MENU_BUTTON));
1031
1032   DrawTextSCentered(326, FONT_TITLE_2, PROGRAM_GAME_BY_STRING);
1033 #endif
1034
1035   DrawPreviewLevel(TRUE);
1036
1037   HandleMainMenu(0, 0, 0, 0, MB_MENU_INITIALIZE);
1038
1039   TapeStop();
1040   if (TAPE_IS_EMPTY(tape))
1041     LoadTape(level_nr);
1042   DrawCompleteVideoDisplay();
1043
1044   PlayMenuSound();
1045   PlayMenuMusic();
1046
1047   /* create gadgets for main menu screen */
1048   FreeScreenGadgets();
1049   CreateScreenGadgets();
1050
1051   /* map gadgets for main menu screen */
1052   MapTapeButtons();
1053   MapScreenMenuGadgets(SCREEN_MASK_MAIN);
1054
1055   DrawMaskedBorder(REDRAW_ALL);
1056
1057   if (do_fading)
1058     FadeIn(redraw_mask);
1059   else
1060     BackToFront();
1061
1062   SetMouseCursor(CURSOR_DEFAULT);
1063
1064   InitAnimation();
1065
1066   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
1067 }
1068
1069 void DrawAndFadeInMainMenu(int redraw_mask)
1070 {
1071   DrawMainMenuExt(redraw_mask, TRUE);
1072 }
1073
1074 void DrawMainMenu()
1075 {
1076   DrawMainMenuExt(REDRAW_ALL, FALSE);
1077 }
1078
1079 #if 0
1080 static void gotoTopLevelDir()
1081 {
1082   /* move upwards to top level directory */
1083   while (leveldir_current->node_parent)
1084   {
1085     /* write a "path" into level tree for easy navigation to last level */
1086     if (leveldir_current->node_parent->node_group->cl_first == -1)
1087     {
1088       int num_leveldirs = numTreeInfoInGroup(leveldir_current);
1089       int leveldir_pos = posTreeInfo(leveldir_current);
1090       int num_page_entries;
1091       int cl_first, cl_cursor;
1092
1093       if (num_leveldirs <= NUM_MENU_ENTRIES_ON_SCREEN)
1094         num_page_entries = num_leveldirs;
1095       else
1096         num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
1097
1098       cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
1099       cl_cursor = leveldir_pos - cl_first;
1100
1101       leveldir_current->node_parent->node_group->cl_first = cl_first;
1102       leveldir_current->node_parent->node_group->cl_cursor = cl_cursor;
1103     }
1104
1105     leveldir_current = leveldir_current->node_parent;
1106   }
1107 }
1108 #endif
1109
1110 #if 1
1111 void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
1112 {
1113   static unsigned long title_delay = 0;
1114   static int title_screen_nr = 0;
1115   boolean return_to_main_menu = FALSE;
1116   boolean use_fading_main_menu = TRUE;
1117   boolean use_cross_fading = !show_title_initial;               /* default */
1118   struct TitleControlInfo *tci;
1119
1120   if (button == MB_MENU_INITIALIZE)
1121   {
1122     int last_game_status = game_status; /* save current game status */
1123
1124     title_delay = 0;
1125     title_screen_nr = 0;
1126     tci = &title_controls[title_screen_nr];
1127
1128     /* determine number of title screens to display (images and messages) */
1129     InitializeTitleControls();
1130
1131     if (game_status == GAME_MODE_INFO)
1132     {
1133       if (num_title_screens == 0)
1134       {
1135         DrawInfoScreen_NotAvailable("Title screen information:",
1136                                     "No title screen for this level set.");
1137
1138         title.auto_delay_final = -1;
1139
1140         return;
1141       }
1142
1143       FadeSoundsAndMusic();
1144
1145       FadeOut(REDRAW_ALL);
1146     }
1147
1148     /* force TITLE music on title info screen */
1149     game_status = GAME_MODE_TITLE;
1150
1151     PlayMenuSound();
1152     PlayMenuMusic();
1153
1154     game_status = last_game_status;     /* restore current game status */
1155
1156     if (tci->is_image)
1157     {
1158       DrawTitleScreenImage(tci->local_nr, tci->initial);
1159     }
1160     else
1161     {
1162       DrawTitleScreenMessage(tci->local_nr, tci->initial);
1163
1164       title.fade_delay_final = title.fade_delay;
1165       title.post_delay_final = title.post_delay;
1166       title.auto_delay_final = -1;
1167     }
1168
1169     SetMouseCursor(CURSOR_NONE);
1170
1171     FadeIn(REDRAW_ALL);
1172
1173     DelayReached(&title_delay, 0);      /* reset delay counter */
1174
1175     return;
1176   }
1177
1178   if (title.auto_delay_final > -1 &&
1179       DelayReached(&title_delay, title.auto_delay_final))
1180     button = MB_MENU_CHOICE;
1181
1182   if (button == MB_MENU_LEAVE)
1183   {
1184     return_to_main_menu = TRUE;
1185     use_fading_main_menu = FALSE;
1186   }
1187   else if (button == MB_MENU_CHOICE)
1188   {
1189     int anim_mode;
1190
1191     if (game_status == GAME_MODE_INFO && num_title_screens == 0)
1192     {
1193       FadeOut(REDRAW_FIELD);
1194
1195       info_mode = INFO_MODE_MAIN;
1196       DrawAndFadeInInfoScreen(REDRAW_FIELD);
1197
1198       return;
1199     }
1200
1201     title_screen_nr++;
1202     tci = &title_controls[title_screen_nr];
1203
1204     if (tci->is_image)
1205       anim_mode =
1206         graphic_info[getTitleScreenGraphic(tci->local_nr,
1207                                            tci->initial)].anim_mode;
1208     else
1209       anim_mode = ANIM_FADE;    /* ??? */
1210
1211     use_cross_fading = (anim_mode == ANIM_FADE ? FALSE :
1212                         anim_mode == ANIM_CROSSFADE ? TRUE :
1213                         use_cross_fading);
1214
1215     if (title_screen_nr < num_title_screens)
1216     {
1217       if (!use_cross_fading)
1218         FadeOut(REDRAW_ALL);
1219
1220       if (use_cross_fading)
1221         FadeCrossSaveBackbuffer();
1222
1223       if (tci->is_image)
1224         DrawTitleScreenImage(tci->local_nr, tci->initial);
1225       else
1226         DrawTitleScreenMessage(tci->local_nr, tci->initial);
1227
1228       if (use_cross_fading)
1229         FadeCross(REDRAW_ALL);
1230       else
1231         FadeIn(REDRAW_ALL);
1232
1233       DelayReached(&title_delay, 0);    /* reset delay counter */
1234     }
1235     else
1236     {
1237       FadeSoundsAndMusic();
1238
1239       FadeOut(REDRAW_ALL);
1240
1241       return_to_main_menu = TRUE;
1242     }
1243   }
1244
1245   if (return_to_main_menu)
1246   {
1247     /* show initial title images and messages only once at program start */
1248     show_title_initial = FALSE;
1249
1250     RedrawBackground();
1251
1252     SetMouseCursor(CURSOR_DEFAULT);
1253
1254     if (game_status == GAME_MODE_INFO)
1255     {
1256       OpenDoor(DOOR_CLOSE_1 | DOOR_CLOSE_2 | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
1257
1258       info_mode = INFO_MODE_MAIN;
1259       DrawInfoScreenExt(REDRAW_ALL, use_fading_main_menu);
1260     }
1261     else        /* default: return to main menu */
1262     {
1263       OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
1264
1265       game_status = GAME_MODE_MAIN;
1266       DrawMainMenuExt(REDRAW_ALL, use_fading_main_menu);
1267     }
1268   }
1269 }
1270
1271 #else
1272
1273 void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
1274 {
1275   static unsigned long title_delay = 0;
1276   static int title_nr = 0;
1277   static boolean showing_message = FALSE;
1278   char *filename = getLevelSetMessageFilename();
1279   boolean return_to_main_menu = FALSE;
1280   boolean use_fading_main_menu = TRUE;
1281   boolean use_cross_fading = !show_title_initial;               /* default */
1282   boolean no_title_info = (graphic_info[IMG_TITLESCREEN_1].bitmap == NULL &&
1283                            getLevelSetMessageFilename(1, FALSE) == NULL);
1284
1285   if (button == MB_MENU_INITIALIZE)
1286   {
1287     int last_game_status = game_status; /* save current game status */
1288
1289     title_delay = 0;
1290     title_nr = 0;
1291     showing_message = FALSE;
1292
1293     if (show_title_initial &&
1294         graphic_info[IMG_TITLESCREEN_INITIAL_1].bitmap == NULL &&
1295         getLevelSetMessageFilename(1, TRUE) == NULL)
1296       show_title_initial = FALSE;
1297
1298     if (game_status == GAME_MODE_INFO)
1299     {
1300       if (no_title_info)
1301       {
1302         DrawInfoScreen_NotAvailable("Title screen information:",
1303                                     "No title screen for this level set.");
1304
1305         title.auto_delay_final = -1;
1306
1307         return;
1308       }
1309
1310       FadeSoundsAndMusic();
1311
1312       FadeOut(REDRAW_ALL);
1313     }
1314
1315     /* force TITLE music on title info screen */
1316     game_status = GAME_MODE_TITLE;
1317
1318     PlayMenuSound();
1319     PlayMenuMusic();
1320
1321     game_status = last_game_status;     /* restore current game status */
1322
1323     if (graphic_info[getTitleScreenGraphic(0, show_title_initial)].bitmap != NULL)
1324     {
1325       DrawTitleScreenImage(title_nr, show_title_initial);
1326     }
1327     else
1328     {
1329       DrawTitleScreenMessage(filename);
1330
1331       showing_message = TRUE;
1332
1333       title.fade_delay_final = title.fade_delay;
1334       title.post_delay_final = title.post_delay;
1335       title.auto_delay_final = -1;
1336     }
1337
1338     FadeIn(REDRAW_ALL);
1339
1340     DelayReached(&title_delay, 0);      /* reset delay counter */
1341
1342     return;
1343   }
1344
1345   if (title.auto_delay_final > -1 &&
1346       DelayReached(&title_delay, title.auto_delay_final))
1347     button = MB_MENU_CHOICE;
1348
1349   if (button == MB_MENU_LEAVE)
1350   {
1351     return_to_main_menu = TRUE;
1352     use_fading_main_menu = FALSE;
1353   }
1354   else if (button == MB_MENU_CHOICE)
1355   {
1356     int anim_mode;
1357
1358     if (game_status == GAME_MODE_INFO && no_title_info)
1359     {
1360       FadeOut(REDRAW_FIELD);
1361
1362       info_mode = INFO_MODE_MAIN;
1363       DrawAndFadeInInfoScreen(REDRAW_FIELD);
1364
1365       return;
1366     }
1367
1368     title_nr++;
1369
1370     if (show_title_initial &&
1371         (title_nr >= MAX_NUM_TITLE_IMAGES ||
1372          graphic_info[IMG_TITLESCREEN_INITIAL_1 + title_nr].bitmap == NULL))
1373     {
1374       show_title_initial = FALSE;
1375
1376       title_nr = 0;     /* restart with title screens for current level set */
1377     }
1378
1379     anim_mode = graphic_info[getTitleScreenGraphic(title_nr, show_title_initial)].anim_mode;
1380
1381     use_cross_fading = (anim_mode == ANIM_FADE ? FALSE :
1382                         anim_mode == ANIM_CROSSFADE ? TRUE :
1383                         use_cross_fading);
1384
1385     if (!use_cross_fading)
1386       FadeOut(REDRAW_ALL);
1387
1388     if (title_nr < MAX_NUM_TITLE_IMAGES &&
1389         graphic_info[getTitleScreenGraphic(title_nr, show_title_initial)].bitmap != NULL)
1390     {
1391       if (use_cross_fading)
1392         FadeCrossSaveBackbuffer();
1393
1394       DrawTitleScreenImage(title_nr, show_title_initial);
1395
1396       if (use_cross_fading)
1397         FadeCross(REDRAW_ALL);
1398       else
1399         FadeIn(REDRAW_ALL);
1400
1401       DelayReached(&title_delay, 0);    /* reset delay counter */
1402     }
1403     else if (!showing_message && filename != NULL)
1404     {
1405       if (use_cross_fading)
1406         FadeCrossSaveBackbuffer();
1407
1408       DrawTitleScreenMessage(filename);
1409
1410       if (use_cross_fading)
1411         FadeCross(REDRAW_ALL);
1412       else
1413         FadeIn(REDRAW_ALL);
1414
1415       DelayReached(&title_delay, 0);    /* reset delay counter */
1416
1417       showing_message = TRUE;
1418     }
1419     else
1420     {
1421       FadeSoundsAndMusic();
1422
1423       FadeOut(REDRAW_ALL);
1424
1425       return_to_main_menu = TRUE;
1426     }
1427   }
1428
1429   if (return_to_main_menu)
1430   {
1431     show_title_initial = FALSE;
1432
1433     RedrawBackground();
1434
1435     if (game_status == GAME_MODE_INFO)
1436     {
1437       OpenDoor(DOOR_CLOSE_1 | DOOR_CLOSE_2 | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
1438
1439       info_mode = INFO_MODE_MAIN;
1440       DrawInfoScreenExt(REDRAW_ALL, use_fading_main_menu);
1441     }
1442     else        /* default: return to main menu */
1443     {
1444       OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
1445
1446       game_status = GAME_MODE_MAIN;
1447       DrawMainMenuExt(REDRAW_ALL, use_fading_main_menu);
1448     }
1449   }
1450 }
1451 #endif
1452
1453 void HandleMainMenu_SelectLevel(int step, int direction)
1454 {
1455   int old_level_nr = level_nr;
1456   int new_level_nr;
1457
1458   new_level_nr = old_level_nr + step * direction;
1459   if (new_level_nr < leveldir_current->first_level)
1460     new_level_nr = leveldir_current->first_level;
1461   if (new_level_nr > leveldir_current->last_level)
1462     new_level_nr = leveldir_current->last_level;
1463
1464   if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
1465   {
1466     /* skipping levels is only allowed when trying to skip single level */
1467     if (setup.skip_levels && step == 1 &&
1468         Request("Level still unsolved ! Skip despite handicap ?", REQ_ASK))
1469     {
1470       leveldir_current->handicap_level++;
1471       SaveLevelSetup_SeriesInfo();
1472     }
1473
1474     new_level_nr = leveldir_current->handicap_level;
1475   }
1476
1477   if (new_level_nr != old_level_nr)
1478   {
1479     struct MainControlInfo *mci= getMainControlInfo(MAIN_CONTROL_CURRENT_LEVEL);
1480
1481     PlaySound(SND_MENU_ITEM_SELECTING);
1482
1483     level_nr = new_level_nr;
1484
1485 #if 1
1486     DrawText(mSX + mci->pos_text->x, mSY + mci->pos_text->y,
1487              int2str(level_nr, 3), mci->font_text);
1488 #else
1489     DrawText(mSX + 11 * 32, mSY + 3 * 32, int2str(level_nr, 3), FONT_VALUE_1);
1490 #endif
1491
1492     LoadLevel(level_nr);
1493     DrawPreviewLevel(TRUE);
1494
1495     TapeErase();
1496     LoadTape(level_nr);
1497     DrawCompleteVideoDisplay();
1498
1499     /* needed because DrawPreviewLevel() takes some time */
1500     BackToFront();
1501     SyncDisplay();
1502   }
1503 }
1504
1505 #if 1
1506
1507 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
1508 {
1509   static int choice = MAIN_CONTROL_GAME;
1510   int pos = choice;
1511   int i;
1512
1513   if (button == MB_MENU_INITIALIZE)
1514   {
1515     DrawCursorAndText_Main(choice, TRUE);
1516
1517     return;
1518   }
1519
1520   if (mx || my)         /* mouse input */
1521   {
1522     pos = -1;
1523
1524     for (i = 0; main_controls[i].nr != -1; i++)
1525     {
1526       if (insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY) ||
1527           insideTextPosRect(main_controls[i].pos_text,   mx - mSX, my - mSY) ||
1528           insideMenuPosRect(main_controls[i].pos_input,  mx - mSX, my - mSY))
1529       {
1530         pos = main_controls[i].nr;
1531
1532         break;
1533       }
1534     }
1535   }
1536   else if (dx || dy)    /* keyboard input */
1537   {
1538     if (dx > 0 && (choice == MAIN_CONTROL_INFO ||
1539                    choice == MAIN_CONTROL_SETUP))
1540       button = MB_MENU_CHOICE;
1541     else if (dy)
1542       pos = choice + dy;
1543   }
1544
1545   if (pos == MAIN_CONTROL_LEVELS && dx != 0 && button)
1546   {
1547     HandleMainMenu_SelectLevel(1, dx < 0 ? -1 : +1);
1548   }
1549   else if (pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT)
1550   {
1551     if (button)
1552     {
1553       if (pos != choice)
1554       {
1555         PlaySound(SND_MENU_ITEM_ACTIVATING);
1556
1557         DrawCursorAndText_Main(choice, FALSE);
1558         DrawCursorAndText_Main(pos, TRUE);
1559
1560         choice = pos;
1561       }
1562     }
1563     else
1564     {
1565       PlaySound(SND_MENU_ITEM_SELECTING);
1566
1567       if (pos == MAIN_CONTROL_NAME)
1568       {
1569         game_status = GAME_MODE_PSEUDO_TYPENAME;
1570
1571         HandleTypeName(strlen(setup.player_name), 0);
1572       }
1573       else if (pos == MAIN_CONTROL_LEVELS)
1574       {
1575         if (leveldir_first)
1576         {
1577           game_status = GAME_MODE_LEVELS;
1578
1579           SaveLevelSetup_LastSeries();
1580           SaveLevelSetup_SeriesInfo();
1581
1582 #if 0
1583           gotoTopLevelDir();
1584 #endif
1585
1586           DrawChooseLevel();
1587         }
1588       }
1589       else if (pos == MAIN_CONTROL_SCORES)
1590       {
1591         game_status = GAME_MODE_SCORES;
1592
1593         DrawHallOfFame(-1);
1594       }
1595       else if (pos == MAIN_CONTROL_EDITOR)
1596       {
1597         if (leveldir_current->readonly &&
1598             !strEqual(setup.player_name, "Artsoft"))
1599           Request("This level is read only !", REQ_CONFIRM);
1600
1601         game_status = GAME_MODE_EDITOR;
1602
1603         DrawLevelEd();
1604       }
1605       else if (pos == MAIN_CONTROL_INFO)
1606       {
1607         game_status = GAME_MODE_INFO;
1608         info_mode = INFO_MODE_MAIN;
1609
1610         DrawInfoScreen();
1611       }
1612       else if (pos == MAIN_CONTROL_GAME)
1613       {
1614         StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
1615       }
1616       else if (pos == MAIN_CONTROL_SETUP)
1617       {
1618         game_status = GAME_MODE_SETUP;
1619         setup_mode = SETUP_MODE_MAIN;
1620
1621         DrawSetupScreen();
1622       }
1623       else if (pos == MAIN_CONTROL_QUIT)
1624       {
1625         SaveLevelSetup_LastSeries();
1626         SaveLevelSetup_SeriesInfo();
1627
1628         if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
1629           game_status = GAME_MODE_QUIT;
1630       }
1631     }
1632   }
1633
1634   if (game_status == GAME_MODE_MAIN)
1635   {
1636     DrawPreviewLevel(FALSE);
1637     DoAnimation();
1638   }
1639 }
1640
1641 #else
1642
1643 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
1644 {
1645   static int choice = 5;
1646   int x = 0;
1647   int y = choice;
1648
1649   if (button == MB_MENU_INITIALIZE)
1650   {
1651     drawCursor(choice, TRUE);
1652
1653     return;
1654   }
1655
1656   if (mx || my)         /* mouse input */
1657   {
1658     x = (mx - mSX) / 32;
1659     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
1660   }
1661   else if (dx || dy)    /* keyboard input */
1662   {
1663     if (dx && choice == 1)
1664       x = (dx < 0 ? 10 : 14);
1665     else if (dx > 0)
1666     {
1667       if (choice == 4 || choice == 6)
1668         button = MB_MENU_CHOICE;
1669     }
1670     else if (dy)
1671       y = choice + dy;
1672   }
1673
1674   if (y == 1 && dx != 0 && button)
1675   {
1676     HandleMainMenu_SelectLevel(1, dx < 0 ? -1 : +1);
1677   }
1678   else if (IN_VIS_FIELD(x, y) &&
1679            y >= 0 && y <= 7 && (y != 1 || x < 10))
1680   {
1681     if (button)
1682     {
1683       if (y != choice)
1684       {
1685         drawCursor(choice, FALSE);
1686         drawCursor(y, TRUE);
1687
1688         choice = y;
1689       }
1690     }
1691     else
1692     {
1693       if (y == 0)
1694       {
1695         game_status = GAME_MODE_PSEUDO_TYPENAME;
1696         HandleTypeName(strlen(setup.player_name), 0);
1697       }
1698       else if (y == 1)
1699       {
1700         if (leveldir_first)
1701         {
1702           game_status = GAME_MODE_LEVELS;
1703           SaveLevelSetup_LastSeries();
1704           SaveLevelSetup_SeriesInfo();
1705
1706 #if 0
1707           gotoTopLevelDir();
1708 #endif
1709
1710           DrawChooseLevel();
1711         }
1712       }
1713       else if (y == 2)
1714       {
1715         game_status = GAME_MODE_SCORES;
1716         DrawHallOfFame(-1);
1717       }
1718       else if (y == 3)
1719       {
1720         if (leveldir_current->readonly &&
1721             !strEqual(setup.player_name, "Artsoft"))
1722           Request("This level is read only !", REQ_CONFIRM);
1723         game_status = GAME_MODE_EDITOR;
1724         DrawLevelEd();
1725       }
1726       else if (y == 4)
1727       {
1728         game_status = GAME_MODE_INFO;
1729         info_mode = INFO_MODE_MAIN;
1730         DrawInfoScreen();
1731       }
1732       else if (y == 5)
1733       {
1734         StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
1735       }
1736       else if (y == 6)
1737       {
1738         game_status = GAME_MODE_SETUP;
1739         setup_mode = SETUP_MODE_MAIN;
1740
1741         DrawSetupScreen();
1742       }
1743       else if (y == 7)
1744       {
1745         SaveLevelSetup_LastSeries();
1746         SaveLevelSetup_SeriesInfo();
1747
1748         if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
1749           game_status = GAME_MODE_QUIT;
1750       }
1751     }
1752   }
1753
1754   if (game_status == GAME_MODE_MAIN)
1755   {
1756     DrawPreviewLevel(FALSE);
1757     DoAnimation();
1758   }
1759 }
1760
1761 #endif
1762
1763
1764 /* ========================================================================= */
1765 /* info screen functions                                                     */
1766 /* ========================================================================= */
1767
1768 static struct TokenInfo *info_info;
1769 static int num_info_info;
1770
1771 static void execInfoTitleScreen()
1772 {
1773   info_mode = INFO_MODE_TITLE;
1774   DrawInfoScreen();
1775 }
1776
1777 static void execInfoElements()
1778 {
1779   info_mode = INFO_MODE_ELEMENTS;
1780   DrawInfoScreen();
1781 }
1782
1783 static void execInfoMusic()
1784 {
1785   info_mode = INFO_MODE_MUSIC;
1786   DrawInfoScreen();
1787 }
1788
1789 static void execInfoCredits()
1790 {
1791   info_mode = INFO_MODE_CREDITS;
1792   DrawInfoScreen();
1793 }
1794
1795 static void execInfoProgram()
1796 {
1797   info_mode = INFO_MODE_PROGRAM;
1798   DrawInfoScreen();
1799 }
1800
1801 static void execInfoVersion()
1802 {
1803   info_mode = INFO_MODE_VERSION;
1804   DrawInfoScreen();
1805 }
1806
1807 static void execInfoLevelSet()
1808 {
1809   info_mode = INFO_MODE_LEVELSET;
1810   DrawInfoScreen();
1811 }
1812
1813 static void execExitInfo()
1814 {
1815   game_status = GAME_MODE_MAIN;
1816   DrawMainMenu();
1817 }
1818
1819 static struct TokenInfo info_info_main[] =
1820 {
1821   { TYPE_ENTER_SCREEN,  execInfoTitleScreen,    "Title Screen"          },
1822   { TYPE_ENTER_SCREEN,  execInfoElements,       "Elements Info"         },
1823   { TYPE_ENTER_SCREEN,  execInfoMusic,          "Music Info"            },
1824   { TYPE_ENTER_SCREEN,  execInfoCredits,        "Credits"               },
1825   { TYPE_ENTER_SCREEN,  execInfoProgram,        "Program Info"          },
1826   { TYPE_ENTER_SCREEN,  execInfoVersion,        "Version Info"          },
1827   { TYPE_ENTER_SCREEN,  execInfoLevelSet,       "Level Set Info"        },
1828   { TYPE_EMPTY,         NULL,                   ""                      },
1829   { TYPE_LEAVE_MENU,    execExitInfo,           "Exit"                  },
1830
1831   { 0,                  NULL,                   NULL                    }
1832 };
1833
1834 static void DrawCursorAndText_Info(int pos, boolean active)
1835 {
1836   int xpos = MENU_SCREEN_START_XPOS;
1837   int ypos = MENU_SCREEN_START_YPOS + pos;
1838   int font_nr = FONT_MENU_1;
1839
1840   if (active)
1841     font_nr = FONT_ACTIVE(font_nr);
1842
1843   DrawText(mSX + xpos * 32, mSY + ypos * 32, info_info[pos].text, font_nr);
1844
1845   if (info_info[pos].type & ~TYPE_SKIP_ENTRY)
1846     drawCursor(pos, active);
1847 }
1848
1849 static void DrawInfoScreen_Main(int redraw_mask, boolean do_fading)
1850 {
1851   int i;
1852
1853   UnmapAllGadgets();
1854   CloseDoor(DOOR_CLOSE_2);
1855
1856   ClearWindow();
1857
1858   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Info Screen");
1859
1860   info_info = info_info_main;
1861   num_info_info = 0;
1862
1863   for (i = 0; info_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
1864   {
1865 #if 0
1866     int xpos = MENU_SCREEN_START_XPOS;
1867     int ypos = MENU_SCREEN_START_YPOS + i;
1868     int font_nr = FONT_MENU_1;
1869 #endif
1870
1871     if (info_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
1872       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
1873     else if (info_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
1874       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
1875     else if (info_info[i].type & ~TYPE_SKIP_ENTRY)
1876       initCursor(i, IMG_MENU_BUTTON);
1877
1878 #if 1
1879     DrawCursorAndText_Info(i, FALSE);
1880 #else
1881     DrawText(mSX + xpos * 32, mSY + ypos * 32, info_info[i].text, font_nr);
1882 #endif
1883
1884     num_info_info++;
1885   }
1886
1887   HandleInfoScreen_Main(0, 0, 0, 0, MB_MENU_INITIALIZE);
1888
1889   PlayMenuSound();
1890   PlayMenuMusic();
1891
1892   DrawMaskedBorder(REDRAW_ALL);
1893
1894   if (do_fading)
1895     FadeIn(redraw_mask);
1896   else
1897     BackToFront();
1898
1899   InitAnimation();
1900 }
1901
1902 void HandleInfoScreen_Main(int mx, int my, int dx, int dy, int button)
1903 {
1904   static int choice_store[MAX_INFO_MODES];
1905   int choice = choice_store[info_mode];         /* always starts with 0 */
1906   int x = 0;
1907   int y = choice;
1908
1909   if (button == MB_MENU_INITIALIZE)
1910   {
1911     /* advance to first valid menu entry */
1912     while (choice < num_info_info &&
1913            info_info[choice].type & TYPE_SKIP_ENTRY)
1914       choice++;
1915     choice_store[info_mode] = choice;
1916
1917 #if 1
1918     DrawCursorAndText_Info(choice, TRUE);
1919 #else
1920     drawCursor(choice, TRUE);
1921 #endif
1922
1923     return;
1924   }
1925   else if (button == MB_MENU_LEAVE)
1926   {
1927     for (y = 0; y < num_info_info; y++)
1928     {
1929       if (info_info[y].type & TYPE_LEAVE_MENU)
1930       {
1931         void (*menu_callback_function)(void) = info_info[y].value;
1932
1933         menu_callback_function();
1934
1935         break;  /* absolutely needed because function changes 'info_info'! */
1936       }
1937     }
1938
1939     return;
1940   }
1941
1942   if (mx || my)         /* mouse input */
1943   {
1944     x = (mx - mSX) / 32;
1945     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
1946   }
1947   else if (dx || dy)    /* keyboard input */
1948   {
1949     if (dx)
1950     {
1951       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
1952
1953       if (info_info[choice].type & menu_navigation_type ||
1954           info_info[choice].type & TYPE_ENTER_SCREEN ||
1955           info_info[choice].type & TYPE_BOOLEAN_STYLE)
1956         button = MB_MENU_CHOICE;
1957     }
1958     else if (dy)
1959       y = choice + dy;
1960
1961     /* jump to next non-empty menu entry (up or down) */
1962     while (y > 0 && y < num_info_info - 1 &&
1963            info_info[y].type & TYPE_SKIP_ENTRY)
1964       y += dy;
1965   }
1966
1967   if (IN_VIS_FIELD(x, y) &&
1968       y >= 0 && y < num_info_info && info_info[y].type & ~TYPE_SKIP_ENTRY)
1969   {
1970     if (button)
1971     {
1972       if (y != choice)
1973       {
1974         PlaySound(SND_MENU_ITEM_ACTIVATING);
1975
1976 #if 1
1977         DrawCursorAndText_Info(choice, FALSE);
1978         DrawCursorAndText_Info(y, TRUE);
1979 #else
1980         drawCursor(choice, FALSE);
1981         drawCursor(y, TRUE);
1982 #endif
1983
1984         choice = choice_store[info_mode] = y;
1985       }
1986     }
1987     else if (!(info_info[y].type & TYPE_GHOSTED))
1988     {
1989       PlaySound(SND_MENU_ITEM_SELECTING);
1990
1991       if (info_info[y].type & TYPE_ENTER_OR_LEAVE)
1992       {
1993         void (*menu_callback_function)(void) = info_info[choice].value;
1994
1995         menu_callback_function();
1996       }
1997     }
1998   }
1999 }
2000
2001 void DrawInfoScreen_NotAvailable(char *text_title, char *text_error)
2002 {
2003 #if 1
2004   int ystart1 = mSY - SY + 100;
2005   int ystart2 = mSY - SY + 150;
2006   int ybottom = mSY - SY + SYSIZE - 20;
2007 #else
2008   int ystart1 = 100;
2009   int ystart2 = 150;
2010   int ybottom = SYSIZE - 20;
2011 #endif
2012
2013   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_LEVELSET);
2014
2015   FadeOut(REDRAW_FIELD);
2016
2017   ClearWindow();
2018   DrawHeadline();
2019
2020   DrawTextSCentered(ystart1, FONT_TEXT_1, text_title);
2021   DrawTextSCentered(ystart2, FONT_TEXT_2, text_error);
2022
2023   DrawTextSCentered(ybottom, FONT_TEXT_4,
2024                     "Press any key or button for info menu");
2025
2026   FadeIn(REDRAW_FIELD);
2027 }
2028
2029 void DrawInfoScreen_HelpAnim(int start, int max_anims, boolean init)
2030 {
2031   static int infoscreen_step[MAX_INFO_ELEMENTS_ON_SCREEN];
2032   static int infoscreen_frame[MAX_INFO_ELEMENTS_ON_SCREEN];
2033   int xstart = mSX + 16;
2034   int ystart1 = mSY - SY + 100;
2035   int ystart2 = mSY + 64 + 2 * 32;
2036   int ybottom = mSY - SY + SYSIZE - 20;
2037   int ystep = TILEY + 4;
2038   int element, action, direction;
2039   int graphic;
2040   int delay;
2041   int sync_frame;
2042   int i, j;
2043
2044   if (init)
2045   {
2046     for (i = 0; i < MAX_INFO_ELEMENTS_ON_SCREEN; i++)
2047       infoscreen_step[i] = infoscreen_frame[i] = 0;
2048
2049     ClearWindow();
2050     DrawHeadline();
2051
2052     DrawTextSCentered(ystart1, FONT_TEXT_1, "The Game Elements:");
2053
2054     DrawTextSCentered(ybottom, FONT_TEXT_4,
2055                       "Press any key or button for next page");
2056
2057     FrameCounter = 0;
2058   }
2059
2060   i = j = 0;
2061   while (helpanim_info[j].element != HELPANIM_LIST_END)
2062   {
2063     if (i >= start + MAX_INFO_ELEMENTS_ON_SCREEN ||
2064         i >= max_anims)
2065       break;
2066     else if (i < start)
2067     {
2068       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
2069         j++;
2070
2071       j++;
2072       i++;
2073
2074       continue;
2075     }
2076
2077     j += infoscreen_step[i - start];
2078
2079     element = helpanim_info[j].element;
2080     action = helpanim_info[j].action;
2081     direction = helpanim_info[j].direction;
2082
2083     if (element < 0)
2084       element = EL_UNKNOWN;
2085
2086     if (action != -1 && direction != -1)
2087       graphic = el_act_dir2img(element, action, direction);
2088     else if (action != -1)
2089       graphic = el_act2img(element, action);
2090     else if (direction != -1)
2091       graphic = el_dir2img(element, direction);
2092     else
2093       graphic = el2img(element);
2094
2095     delay = helpanim_info[j++].delay;
2096
2097     if (delay == -1)
2098       delay = 1000000;
2099
2100     if (infoscreen_frame[i - start] == 0)
2101     {
2102       sync_frame = 0;
2103       infoscreen_frame[i - start] = delay - 1;
2104     }
2105     else
2106     {
2107       sync_frame = delay - infoscreen_frame[i - start];
2108       infoscreen_frame[i - start]--;
2109     }
2110
2111     if (helpanim_info[j].element == HELPANIM_LIST_NEXT)
2112     {
2113       if (!infoscreen_frame[i - start])
2114         infoscreen_step[i - start] = 0;
2115     }
2116     else
2117     {
2118       if (!infoscreen_frame[i - start])
2119         infoscreen_step[i - start]++;
2120       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
2121         j++;
2122     }
2123
2124     j++;
2125
2126     ClearRectangleOnBackground(drawto, xstart, ystart2 + (i - start) * ystep,
2127                                TILEX, TILEY);
2128     DrawGraphicAnimationExt(drawto, xstart, ystart2 + (i - start) * ystep,
2129                             graphic, sync_frame, USE_MASKING);
2130
2131     if (init)
2132       DrawInfoScreen_HelpText(element, action, direction, i - start);
2133
2134     i++;
2135   }
2136
2137   redraw_mask |= REDRAW_FIELD;
2138
2139   FrameCounter++;
2140 }
2141
2142 static char *getHelpText(int element, int action, int direction)
2143 {
2144   char token[MAX_LINE_LEN];
2145
2146   strcpy(token, element_info[element].token_name);
2147
2148   if (action != -1)
2149     strcat(token, element_action_info[action].suffix);
2150
2151   if (direction != -1)
2152     strcat(token, element_direction_info[MV_DIR_TO_BIT(direction)].suffix);
2153
2154   return getHashEntry(helptext_info, token);
2155 }
2156
2157 void DrawInfoScreen_HelpText(int element, int action, int direction, int ypos)
2158 {
2159 #if 1
2160   int font_nr = FONT_INFO_ELEMENTS;
2161 #else
2162   int font_nr = FONT_LEVEL_NUMBER;
2163 #endif
2164   int font_width = getFontWidth(font_nr);
2165   int sx = mSX + MINI_TILEX + TILEX + MINI_TILEX;
2166   int sy = mSY + 65 + 2 * 32 + 1;
2167   int ystep = TILEY + 4;
2168   int pad_x = sx - SX;
2169   int max_chars_per_line = (SXSIZE - pad_x - MINI_TILEX) / font_width;
2170   int max_lines_per_text = 2;    
2171   char *text = NULL;
2172
2173   if (action != -1 && direction != -1)          /* element.action.direction */
2174     text = getHelpText(element, action, direction);
2175
2176   if (text == NULL && action != -1)             /* element.action */
2177     text = getHelpText(element, action, -1);
2178
2179   if (text == NULL && direction != -1)          /* element.direction */
2180     text = getHelpText(element, -1, direction);
2181
2182   if (text == NULL)                             /* base element */
2183     text = getHelpText(element, -1, -1);
2184
2185   if (text == NULL)                             /* not found */
2186     text = "No description available";
2187
2188   if (strlen(text) <= max_chars_per_line)       /* only one line of text */
2189     sy += getFontHeight(font_nr) / 2;
2190
2191   DrawTextWrapped(sx, sy + ypos * ystep, text, font_nr,
2192                   max_chars_per_line, max_lines_per_text);
2193 }
2194
2195 void DrawInfoScreen_TitleScreen()
2196 {
2197   DrawTitleScreen();
2198 }
2199
2200 void HandleInfoScreen_TitleScreen(int button)
2201 {
2202   HandleTitleScreen(0, 0, 0, 0, button);
2203 }
2204
2205 void DrawInfoScreen_Elements()
2206 {
2207   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_ELEMENTS);
2208
2209   FadeOut(REDRAW_FIELD);
2210
2211   LoadHelpAnimInfo();
2212   LoadHelpTextInfo();
2213
2214   HandleInfoScreen_Elements(MB_MENU_INITIALIZE);
2215
2216   FadeIn(REDRAW_FIELD);
2217
2218   InitAnimation();
2219 }
2220
2221 void HandleInfoScreen_Elements(int button)
2222 {
2223   static unsigned long info_delay = 0;
2224   static int num_anims;
2225   static int num_pages;
2226   static int page;
2227   int anims_per_page = MAX_INFO_ELEMENTS_ON_SCREEN;
2228   int i;
2229
2230   if (button == MB_MENU_INITIALIZE)
2231   {
2232     boolean new_element = TRUE;
2233
2234     num_anims = 0;
2235
2236     for (i = 0; helpanim_info[i].element != HELPANIM_LIST_END; i++)
2237     {
2238       if (helpanim_info[i].element == HELPANIM_LIST_NEXT)
2239         new_element = TRUE;
2240       else if (new_element)
2241       {
2242         num_anims++;
2243         new_element = FALSE;
2244       }
2245     }
2246
2247     num_pages = (num_anims + anims_per_page - 1) / anims_per_page;
2248     page = 0;
2249   }
2250
2251   if (button == MB_MENU_LEAVE)
2252   {
2253     PlaySound(SND_MENU_ITEM_SELECTING);
2254
2255     info_mode = INFO_MODE_MAIN;
2256     DrawInfoScreen();
2257
2258     return;
2259   }
2260   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
2261   {
2262     if (button != MB_MENU_INITIALIZE)
2263     {
2264       PlaySound(SND_MENU_ITEM_SELECTING);
2265
2266       page++;
2267     }
2268
2269     if (page >= num_pages)
2270     {
2271       FadeSoundsAndMusic();
2272       FadeOut(REDRAW_FIELD);
2273
2274       info_mode = INFO_MODE_MAIN;
2275       DrawAndFadeInInfoScreen(REDRAW_FIELD);
2276
2277       return;
2278     }
2279
2280     if (button != MB_MENU_INITIALIZE)
2281       FadeCrossSaveBackbuffer();
2282
2283     DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, TRUE);
2284
2285     if (button != MB_MENU_INITIALIZE)
2286       FadeCross(REDRAW_FIELD);
2287   }
2288   else
2289   {
2290     if (DelayReached(&info_delay, GameFrameDelay))
2291       if (page < num_pages)
2292         DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, FALSE);
2293
2294     PlayMenuSoundIfLoop();
2295   }
2296 }
2297
2298 void DrawInfoScreen_Music()
2299 {
2300   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_MUSIC);
2301
2302   FadeOut(REDRAW_FIELD);
2303
2304   ClearWindow();
2305   DrawHeadline();
2306
2307   LoadMusicInfo();
2308
2309   HandleInfoScreen_Music(MB_MENU_INITIALIZE);
2310
2311   FadeIn(REDRAW_FIELD);
2312 }
2313
2314 void HandleInfoScreen_Music(int button)
2315 {
2316   static struct MusicFileInfo *list = NULL;
2317   int ystart1 = mSY - SY + 100;
2318   int ystart2 = mSY - SY + 150;
2319   int ybottom = mSY - SY + SYSIZE - 20;
2320   int dy = 30;
2321
2322   if (button == MB_MENU_INITIALIZE)
2323   {
2324     list = music_file_info;
2325
2326     if (list == NULL)
2327     {
2328       FadeSoundsAndMusic();
2329
2330       ClearWindow();
2331       DrawHeadline();
2332
2333       DrawTextSCentered(ystart1, FONT_TEXT_1,
2334                         "No music info for this level set.");
2335
2336       DrawTextSCentered(ybottom, FONT_TEXT_4,
2337                         "Press any key or button for info menu");
2338
2339       return;
2340     }
2341   }
2342
2343   if (button == MB_MENU_LEAVE)
2344   {
2345     PlaySound(SND_MENU_ITEM_SELECTING);
2346
2347     FadeSoundsAndMusic();
2348
2349     info_mode = INFO_MODE_MAIN;
2350     DrawInfoScreen();
2351
2352     return;
2353   }
2354   else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE)
2355   {
2356     int y = 0;
2357
2358     if (button != MB_MENU_INITIALIZE)
2359     {
2360       PlaySound(SND_MENU_ITEM_SELECTING);
2361
2362       if (list != NULL)
2363         list = list->next;
2364     }
2365
2366     if (list == NULL)
2367     {
2368       FadeSoundsAndMusic();
2369       FadeOut(REDRAW_FIELD);
2370
2371       info_mode = INFO_MODE_MAIN;
2372       DrawAndFadeInInfoScreen(REDRAW_FIELD);
2373
2374       return;
2375     }
2376
2377     FadeSoundsAndMusic();
2378
2379     if (button != MB_MENU_INITIALIZE)
2380       FadeCrossSaveBackbuffer();
2381
2382     ClearWindow();
2383     DrawHeadline();
2384
2385     if (list->is_sound)
2386     {
2387       int sound = list->music;
2388
2389       if (sound_info[sound].loop)
2390         PlaySoundLoop(sound);
2391       else
2392         PlaySound(sound);
2393
2394       DrawTextSCentered(ystart1, FONT_TEXT_1, "The Game Background Sounds:");
2395     }
2396     else
2397     {
2398       PlayMusic(list->music);
2399
2400       DrawTextSCentered(ystart1, FONT_TEXT_1, "The Game Background Music:");
2401     }
2402
2403     if (!strEqual(list->title, UNKNOWN_NAME))
2404     {
2405       if (!strEqual(list->title_header, UNKNOWN_NAME))
2406         DrawTextSCentered(ystart2 + y++ * dy, FONT_TEXT_2, list->title_header);
2407
2408       DrawTextFCentered(ystart2 + y++ * dy, FONT_TEXT_3, "\"%s\"", list->title);
2409     }
2410
2411     if (!strEqual(list->artist, UNKNOWN_NAME))
2412     {
2413       if (!strEqual(list->artist_header, UNKNOWN_NAME))
2414         DrawTextSCentered(ystart2 + y++ * dy, FONT_TEXT_2, list->artist_header);
2415       else
2416         DrawTextSCentered(ystart2 + y++ * dy, FONT_TEXT_2, "by");
2417
2418       DrawTextFCentered(ystart2 + y++ * dy, FONT_TEXT_3, "%s", list->artist);
2419     }
2420
2421     if (!strEqual(list->album, UNKNOWN_NAME))
2422     {
2423       if (!strEqual(list->album_header, UNKNOWN_NAME))
2424         DrawTextSCentered(ystart2 + y++ * dy, FONT_TEXT_2, list->album_header);
2425       else
2426         DrawTextSCentered(ystart2 + y++ * dy, FONT_TEXT_2, "from the album");
2427
2428       DrawTextFCentered(ystart2 + y++ * dy, FONT_TEXT_3, "\"%s\"", list->album);
2429     }
2430
2431     if (!strEqual(list->year, UNKNOWN_NAME))
2432     {
2433       if (!strEqual(list->year_header, UNKNOWN_NAME))
2434         DrawTextSCentered(ystart2 + y++ * dy, FONT_TEXT_2, list->year_header);
2435       else
2436         DrawTextSCentered(ystart2 + y++ * dy, FONT_TEXT_2, "from the year");
2437
2438       DrawTextFCentered(ystart2 + y++ * dy, FONT_TEXT_3, "%s", list->year);
2439     }
2440
2441     DrawTextSCentered(ybottom, FONT_TEXT_4,
2442                       "Press any key or button for next page");
2443
2444     if (button != MB_MENU_INITIALIZE)
2445       FadeCross(REDRAW_FIELD);
2446   }
2447
2448   if (list != NULL && list->is_sound && sound_info[list->music].loop)
2449     PlaySoundLoop(list->music);
2450 }
2451
2452 static boolean DrawInfoScreen_CreditsScreen(int screen_nr)
2453 {
2454   int ystart1 = mSY - SY + 100;
2455   int ystart2 = mSY - SY + 150;
2456   int ybottom = mSY - SY + SYSIZE - 20;
2457   int ystep = 30;
2458
2459   if (screen_nr > 8)
2460     return FALSE;
2461
2462   ClearWindow();
2463   DrawHeadline();
2464
2465   DrawTextSCentered(ystart1, FONT_TEXT_1, "Credits:");
2466
2467   if (screen_nr == 0)
2468   {
2469     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2470                       "Special thanks to");
2471     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_3,
2472                       "Peter Liepa");
2473     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_2,
2474                       "for creating");
2475     DrawTextSCentered(ystart2 + 3 * ystep, FONT_TEXT_3,
2476                       "\"Boulder Dash\"");
2477     DrawTextSCentered(ystart2 + 4 * ystep, FONT_TEXT_2,
2478                       "in the year");
2479     DrawTextSCentered(ystart2 + 5 * ystep, FONT_TEXT_3,
2480                       "1984");
2481     DrawTextSCentered(ystart2 + 6 * ystep, FONT_TEXT_2,
2482                       "published by");
2483     DrawTextSCentered(ystart2 + 7 * ystep, FONT_TEXT_3,
2484                       "First Star Software");
2485   }
2486   else if (screen_nr == 1)
2487   {
2488     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2489                       "Special thanks to");
2490     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_3,
2491                       "Klaus Heinz & Volker Wertich");
2492     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_2,
2493                       "for creating");
2494     DrawTextSCentered(ystart2 + 3 * ystep, FONT_TEXT_3,
2495                       "\"Emerald Mine\"");
2496     DrawTextSCentered(ystart2 + 4 * ystep, FONT_TEXT_2,
2497                       "in the year");
2498     DrawTextSCentered(ystart2 + 5 * ystep, FONT_TEXT_3,
2499                       "1987");
2500     DrawTextSCentered(ystart2 + 6 * ystep, FONT_TEXT_2,
2501                       "published by");
2502     DrawTextSCentered(ystart2 + 7 * ystep, FONT_TEXT_3,
2503                       "Kingsoft");
2504   }
2505   else if (screen_nr == 2)
2506   {
2507     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2508                       "Special thanks to");
2509     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_3,
2510                       "Michael Stopp & Philip Jespersen");
2511     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_2,
2512                       "for creating");
2513     DrawTextSCentered(ystart2 + 3 * ystep, FONT_TEXT_3,
2514                       "\"Supaplex\"");
2515     DrawTextSCentered(ystart2 + 4 * ystep, FONT_TEXT_2,
2516                       "in the year");
2517     DrawTextSCentered(ystart2 + 5 * ystep, FONT_TEXT_3,
2518                       "1991");
2519     DrawTextSCentered(ystart2 + 6 * ystep, FONT_TEXT_2,
2520                       "published by");
2521     DrawTextSCentered(ystart2 + 7 * ystep, FONT_TEXT_3,
2522                       "Digital Integration");
2523   }
2524   else if (screen_nr == 3)
2525   {
2526     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2527                       "Special thanks to");
2528     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_3,
2529                       "Hiroyuki Imabayashi");
2530     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_2,
2531                       "for creating");
2532     DrawTextSCentered(ystart2 + 3 * ystep, FONT_TEXT_3,
2533                       "\"Sokoban\"");
2534     DrawTextSCentered(ystart2 + 4 * ystep, FONT_TEXT_2,
2535                       "in the year");
2536     DrawTextSCentered(ystart2 + 5 * ystep, FONT_TEXT_3,
2537                       "1982");
2538     DrawTextSCentered(ystart2 + 6 * ystep, FONT_TEXT_2,
2539                       "published by");
2540     DrawTextSCentered(ystart2 + 7 * ystep, FONT_TEXT_3,
2541                       "Thinking Rabbit");
2542   }
2543   else if (screen_nr == 4)
2544   {
2545     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2546                       "Special thanks to");
2547     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_3,
2548                       "Alan Bond");
2549     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_2,
2550                       "and");
2551     DrawTextSCentered(ystart2 + 3 * ystep, FONT_TEXT_3,
2552                       "Jürgen Bonhagen");
2553     DrawTextSCentered(ystart2 + 4 * ystep, FONT_TEXT_2,
2554                       "for the continuous creation");
2555     DrawTextSCentered(ystart2 + 5 * ystep, FONT_TEXT_2,
2556                       "of outstanding level sets");
2557   }
2558   else if (screen_nr == 5)
2559   {
2560     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2561                       "Thanks to");
2562     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_3,
2563                       "Peter Elzner");
2564     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_2,
2565                       "for ideas and inspiration by");
2566     DrawTextSCentered(ystart2 + 3 * ystep, FONT_TEXT_3,
2567                       "Diamond Caves");
2568
2569     DrawTextSCentered(ystart2 + 5 * ystep, FONT_TEXT_2,
2570                       "Thanks to");
2571     DrawTextSCentered(ystart2 + 6 * ystep, FONT_TEXT_3,
2572                       "Steffest");
2573     DrawTextSCentered(ystart2 + 7 * ystep, FONT_TEXT_2,
2574                       "for ideas and inspiration by");
2575     DrawTextSCentered(ystart2 + 8 * ystep, FONT_TEXT_3,
2576                       "DX-Boulderdash");
2577   }
2578   else if (screen_nr == 6)
2579   {
2580     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2581                       "Thanks to");
2582     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_3,
2583                       "David Tritscher");
2584     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_2,
2585                       "for the new Emerald Mine engine");
2586   }
2587   else if (screen_nr == 7)
2588   {
2589     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2590                       "Thanks to");
2591     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_3,
2592                       "Guido Schulz");
2593     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_2,
2594                       "for the initial DOS port");
2595
2596     DrawTextSCentered(ystart2 + 4 * ystep, FONT_TEXT_2,
2597                       "Thanks to");
2598     DrawTextSCentered(ystart2 + 5 * ystep, FONT_TEXT_3,
2599                       "Karl Hörnell");
2600     DrawTextSCentered(ystart2 + 6 * ystep, FONT_TEXT_2,
2601                       "for some additional toons");
2602   }
2603   else if (screen_nr == 8)
2604   {
2605     DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2606                       "And not to forget:");
2607     DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_2,
2608                       "Many thanks to");
2609     DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_3,
2610                       "All those who contributed");
2611     DrawTextSCentered(ystart2 + 3 * ystep, FONT_TEXT_3,
2612                       "levels to this game");
2613     DrawTextSCentered(ystart2 + 4 * ystep, FONT_TEXT_3,
2614                       "since 1995");
2615   }
2616 #if 0
2617   else
2618   {
2619     return FALSE;
2620   }
2621 #endif
2622
2623   DrawTextSCentered(ybottom, FONT_TEXT_4,
2624                     "Press any key or button for next page");
2625
2626   return TRUE;
2627 }
2628
2629 void DrawInfoScreen_Credits()
2630 {
2631   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_CREDITS);
2632
2633   FadeSoundsAndMusic();
2634
2635   FadeOut(REDRAW_FIELD);
2636
2637   HandleInfoScreen_Credits(MB_MENU_INITIALIZE);
2638
2639   FadeIn(REDRAW_FIELD);
2640 }
2641
2642 void HandleInfoScreen_Credits(int button)
2643 {
2644   static int screen_nr = 0;
2645
2646   if (button == MB_MENU_INITIALIZE)
2647   {
2648     screen_nr = 0;
2649
2650     DrawInfoScreen_CreditsScreen(screen_nr);
2651   }
2652   else if (button == MB_MENU_LEAVE)
2653   {
2654     PlaySound(SND_MENU_ITEM_SELECTING);
2655
2656     info_mode = INFO_MODE_MAIN;
2657     DrawInfoScreen();
2658
2659     return;
2660   }
2661   else if (button == MB_MENU_CHOICE)
2662   {
2663     boolean show_screen;
2664
2665     PlaySound(SND_MENU_ITEM_SELECTING);
2666
2667     screen_nr++;
2668
2669     FadeCrossSaveBackbuffer();
2670
2671     show_screen = DrawInfoScreen_CreditsScreen(screen_nr);
2672   
2673     if (show_screen)
2674     {
2675       FadeCross(REDRAW_FIELD);
2676     }
2677     else
2678     {
2679       FadeSoundsAndMusic();
2680       FadeOut(REDRAW_FIELD);
2681
2682       info_mode = INFO_MODE_MAIN;
2683       DrawAndFadeInInfoScreen(REDRAW_FIELD);
2684     }
2685   }
2686   else
2687   {
2688     PlayMenuSoundIfLoop();
2689   }
2690 }
2691
2692 void DrawInfoScreen_Program()
2693 {
2694   int ystart1 = mSY - SY + 100;
2695   int ystart2 = mSY - SY + 150;
2696   int ybottom = mSY - SY + SYSIZE - 20;
2697   int ystep = 30;
2698
2699   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_PROGRAM);
2700
2701   FadeOut(REDRAW_FIELD);
2702
2703   ClearWindow();
2704   DrawHeadline();
2705
2706   DrawTextSCentered(ystart1, FONT_TEXT_1, "Program Information:");
2707
2708   DrawTextSCentered(ystart2 + 0 * ystep, FONT_TEXT_2,
2709                     "This game is Freeware!");
2710   DrawTextSCentered(ystart2 + 1 * ystep, FONT_TEXT_2,
2711                     "If you like it, send e-mail to:");
2712   DrawTextSCentered(ystart2 + 2 * ystep, FONT_TEXT_3,
2713                     PROGRAM_EMAIL_STRING);
2714   DrawTextSCentered(ystart2 + 3 * ystep, FONT_TEXT_2,
2715                     "or SnailMail to:");
2716   DrawTextSCentered(ystart2 + 4 * ystep + 0, FONT_TEXT_3,
2717                     "Holger Schemel");
2718   DrawTextSCentered(ystart2 + 4 * ystep + 20, FONT_TEXT_3,
2719                     "Detmolder Strasse 189");
2720   DrawTextSCentered(ystart2 + 4 * ystep + 40, FONT_TEXT_3,
2721                     "33604 Bielefeld");
2722   DrawTextSCentered(ystart2 + 4 * ystep + 60, FONT_TEXT_3,
2723                     "Germany");
2724   DrawTextSCentered(ystart2 + 7 * ystep, FONT_TEXT_2,
2725                     "More information and levels:");
2726   DrawTextSCentered(ystart2 + 8 * ystep, FONT_TEXT_3,
2727                     PROGRAM_WEBSITE_STRING);
2728   DrawTextSCentered(ystart2 + 9 * ystep, FONT_TEXT_2,
2729                     "If you have created new levels,");
2730   DrawTextSCentered(ystart2 + 10 * ystep, FONT_TEXT_2,
2731                     "send them to me to include them!");
2732   DrawTextSCentered(ystart2 + 11 * ystep, FONT_TEXT_2,
2733                     ":-)");
2734
2735   DrawTextSCentered(ybottom, FONT_TEXT_4,
2736                     "Press any key or button for info menu");
2737
2738   FadeIn(REDRAW_FIELD);
2739 }
2740
2741 void HandleInfoScreen_Program(int button)
2742 {
2743   if (button == MB_MENU_LEAVE)
2744   {
2745     PlaySound(SND_MENU_ITEM_SELECTING);
2746
2747     info_mode = INFO_MODE_MAIN;
2748     DrawInfoScreen();
2749
2750     return;
2751   }
2752   else if (button == MB_MENU_CHOICE)
2753   {
2754     PlaySound(SND_MENU_ITEM_SELECTING);
2755
2756     FadeSoundsAndMusic();
2757     FadeOut(REDRAW_FIELD);
2758
2759     info_mode = INFO_MODE_MAIN;
2760     DrawAndFadeInInfoScreen(REDRAW_FIELD);
2761   }
2762   else
2763   {
2764     PlayMenuSoundIfLoop();
2765   }
2766 }
2767
2768 void DrawInfoScreen_Version()
2769 {
2770   int font_header = FONT_TEXT_3;
2771   int font_text = FONT_TEXT_2;
2772   int xstep = getFontWidth(font_text);
2773   int ystep = getFontHeight(font_text);
2774   int ystart1 = mSY - SY + 100;
2775   int ystart2 = mSY - SY + 150;
2776   int ybottom = mSY - SY + SYSIZE - 20;
2777   int xstart1 = mSX + 2 * xstep;
2778   int xstart2 = mSX + 18 * xstep;
2779 #if defined(TARGET_SDL)
2780   int xstart3 = mSX + 28 * xstep;
2781   SDL_version sdl_version_compiled;
2782   const SDL_version *sdl_version_linked;
2783 #endif
2784
2785   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_PROGRAM);
2786
2787   FadeOut(REDRAW_FIELD);
2788
2789   ClearWindow();
2790   DrawHeadline();
2791
2792   DrawTextSCentered(ystart1, FONT_TEXT_1, "Version Information:");
2793
2794   DrawTextF(xstart1, ystart2, font_header, "Name");
2795   DrawTextF(xstart2, ystart2, font_text, PROGRAM_TITLE_STRING);
2796
2797   ystart2 += ystep;
2798   DrawTextF(xstart1, ystart2, font_header, "Version");
2799   DrawTextF(xstart2, ystart2, font_text, getProgramFullVersionString());
2800
2801   ystart2 += ystep;
2802   DrawTextF(xstart1, ystart2, font_header, "Platform");
2803   DrawTextF(xstart2, ystart2, font_text, PLATFORM_STRING);
2804
2805   ystart2 += ystep;
2806   DrawTextF(xstart1, ystart2, font_header, "Target");
2807   DrawTextF(xstart2, ystart2, font_text, TARGET_STRING);
2808
2809   ystart2 += ystep;
2810   DrawTextF(xstart1, ystart2, font_header, "Compile time");
2811   DrawTextF(xstart2, ystart2, font_text, getCompileDateString());
2812
2813 #if defined(TARGET_SDL)
2814   ystart2 += 3 * ystep;
2815   DrawTextF(xstart1, ystart2, font_header, "Library");
2816   DrawTextF(xstart2, ystart2, font_header, "compiled");
2817   DrawTextF(xstart3, ystart2, font_header, "linked");
2818
2819   SDL_VERSION(&sdl_version_compiled);
2820   sdl_version_linked = SDL_Linked_Version();
2821
2822   ystart2 += 2 * ystep;
2823   DrawTextF(xstart1, ystart2, font_text, "SDL");
2824   DrawTextF(xstart2, ystart2, font_text, "%d.%d.%d",
2825             sdl_version_compiled.major,
2826             sdl_version_compiled.minor,
2827             sdl_version_compiled.patch);
2828   DrawTextF(xstart3, ystart2, font_text, "%d.%d.%d",
2829             sdl_version_linked->major,
2830             sdl_version_linked->minor,
2831             sdl_version_linked->patch);
2832
2833   SDL_IMAGE_VERSION(&sdl_version_compiled);
2834   sdl_version_linked = IMG_Linked_Version();
2835
2836   ystart2 += ystep;
2837   DrawTextF(xstart1, ystart2, font_text, "SDL_image");
2838   DrawTextF(xstart2, ystart2, font_text, "%d.%d.%d",
2839             sdl_version_compiled.major,
2840             sdl_version_compiled.minor,
2841             sdl_version_compiled.patch);
2842   DrawTextF(xstart3, ystart2, font_text, "%d.%d.%d",
2843             sdl_version_linked->major,
2844             sdl_version_linked->minor,
2845             sdl_version_linked->patch);
2846
2847   SDL_MIXER_VERSION(&sdl_version_compiled);
2848   sdl_version_linked = Mix_Linked_Version();
2849
2850   ystart2 += ystep;
2851   DrawTextF(xstart1, ystart2, font_text, "SDL_mixer");
2852   DrawTextF(xstart2, ystart2, font_text, "%d.%d.%d",
2853             sdl_version_compiled.major,
2854             sdl_version_compiled.minor,
2855             sdl_version_compiled.patch);
2856   DrawTextF(xstart3, ystart2, font_text, "%d.%d.%d",
2857             sdl_version_linked->major,
2858             sdl_version_linked->minor,
2859             sdl_version_linked->patch);
2860
2861   SDL_NET_VERSION(&sdl_version_compiled);
2862   sdl_version_linked = SDLNet_Linked_Version();
2863
2864   ystart2 += ystep;
2865   DrawTextF(xstart1, ystart2, font_text, "SDL_net");
2866   DrawTextF(xstart2, ystart2, font_text, "%d.%d.%d",
2867             sdl_version_compiled.major,
2868             sdl_version_compiled.minor,
2869             sdl_version_compiled.patch);
2870   DrawTextF(xstart3, ystart2, font_text, "%d.%d.%d",
2871             sdl_version_linked->major,
2872             sdl_version_linked->minor,
2873             sdl_version_linked->patch);
2874 #endif
2875
2876   DrawTextSCentered(ybottom, FONT_TEXT_4,
2877                     "Press any key or button for info menu");
2878
2879   FadeIn(REDRAW_FIELD);
2880 }
2881
2882 void HandleInfoScreen_Version(int button)
2883 {
2884   if (button == MB_MENU_LEAVE)
2885   {
2886     PlaySound(SND_MENU_ITEM_SELECTING);
2887
2888     info_mode = INFO_MODE_MAIN;
2889     DrawInfoScreen();
2890
2891     return;
2892   }
2893   else if (button == MB_MENU_CHOICE)
2894   {
2895     PlaySound(SND_MENU_ITEM_SELECTING);
2896
2897     FadeSoundsAndMusic();
2898     FadeOut(REDRAW_FIELD);
2899
2900     info_mode = INFO_MODE_MAIN;
2901     DrawAndFadeInInfoScreen(REDRAW_FIELD);
2902   }
2903   else
2904   {
2905     PlayMenuSoundIfLoop();
2906   }
2907 }
2908
2909 void DrawInfoScreen_LevelSet()
2910 {
2911   int ystart1 = mSY - SY + 100;
2912   int ystart2 = mSY - SY + 150;
2913   int ybottom = mSY - SY + SYSIZE - 20;
2914   char *filename = getLevelSetInfoFilename();
2915 #if 1
2916   int font_nr = FONT_INFO_LEVELSET;
2917 #else
2918   int font_nr = FONT_LEVEL_NUMBER;
2919 #endif
2920   int font_width = getFontWidth(font_nr);
2921   int font_height = getFontHeight(font_nr);
2922   int pad_x = 32;
2923   int pad_y = 150;
2924   int sx = mSX + pad_x;
2925   int sy = mSY + pad_y;
2926   int max_chars_per_line = (SXSIZE - 2 * pad_x) / font_width;
2927   int max_lines_per_screen = (SYSIZE - pad_y) / font_height - 1;
2928
2929   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_LEVELSET);
2930
2931   FadeOut(REDRAW_FIELD);
2932
2933   ClearWindow();
2934   DrawHeadline();
2935
2936   DrawTextSCentered(ystart1, FONT_TEXT_1, "Level Set Information:");
2937
2938   DrawTextSCentered(ybottom, FONT_TEXT_4,
2939                     "Press any key or button for info menu");
2940
2941   if (filename != NULL)
2942     DrawTextFromFile(sx, sy, filename, font_nr, max_chars_per_line,
2943                      max_lines_per_screen, TRUE);
2944   else
2945     DrawTextSCentered(ystart2, FONT_TEXT_2,
2946                       "No information for this level set.");
2947
2948   FadeIn(REDRAW_FIELD);
2949 }
2950
2951 void HandleInfoScreen_LevelSet(int button)
2952 {
2953   if (button == MB_MENU_LEAVE)
2954   {
2955     PlaySound(SND_MENU_ITEM_SELECTING);
2956
2957     info_mode = INFO_MODE_MAIN;
2958     DrawInfoScreen();
2959
2960     return;
2961   }
2962   else if (button == MB_MENU_CHOICE)
2963   {
2964     PlaySound(SND_MENU_ITEM_SELECTING);
2965
2966     FadeSoundsAndMusic();
2967     FadeOut(REDRAW_FIELD);
2968
2969     info_mode = INFO_MODE_MAIN;
2970     DrawAndFadeInInfoScreen(REDRAW_FIELD);
2971   }
2972   else
2973   {
2974     PlayMenuSoundIfLoop();
2975   }
2976 }
2977
2978 static void DrawInfoScreenExt(int redraw_mask, boolean do_fading)
2979 {
2980   SetMainBackgroundImage(IMG_BACKGROUND_INFO);
2981
2982   if (info_mode == INFO_MODE_TITLE)
2983     DrawInfoScreen_TitleScreen();
2984   else if (info_mode == INFO_MODE_ELEMENTS)
2985     DrawInfoScreen_Elements();
2986   else if (info_mode == INFO_MODE_MUSIC)
2987     DrawInfoScreen_Music();
2988   else if (info_mode == INFO_MODE_CREDITS)
2989     DrawInfoScreen_Credits();
2990   else if (info_mode == INFO_MODE_PROGRAM)
2991     DrawInfoScreen_Program();
2992   else if (info_mode == INFO_MODE_VERSION)
2993     DrawInfoScreen_Version();
2994   else if (info_mode == INFO_MODE_LEVELSET)
2995     DrawInfoScreen_LevelSet();
2996   else
2997     DrawInfoScreen_Main(redraw_mask, do_fading);
2998
2999   if (info_mode != INFO_MODE_MAIN &&
3000       info_mode != INFO_MODE_TITLE &&
3001       info_mode != INFO_MODE_MUSIC)
3002   {
3003     PlayMenuSound();
3004     PlayMenuMusic();
3005   }
3006 }
3007
3008 void DrawAndFadeInInfoScreen(int redraw_mask)
3009 {
3010   DrawInfoScreenExt(redraw_mask, TRUE);
3011 }
3012
3013 void DrawInfoScreen()
3014 {
3015   DrawInfoScreenExt(REDRAW_ALL, FALSE);
3016 }
3017
3018 void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
3019 {
3020   if (info_mode == INFO_MODE_TITLE)
3021     HandleInfoScreen_TitleScreen(button);
3022   else if (info_mode == INFO_MODE_ELEMENTS)
3023     HandleInfoScreen_Elements(button);
3024   else if (info_mode == INFO_MODE_MUSIC)
3025     HandleInfoScreen_Music(button);
3026   else if (info_mode == INFO_MODE_CREDITS)
3027     HandleInfoScreen_Credits(button);
3028   else if (info_mode == INFO_MODE_PROGRAM)
3029     HandleInfoScreen_Program(button);
3030   else if (info_mode == INFO_MODE_VERSION)
3031     HandleInfoScreen_Version(button);
3032   else if (info_mode == INFO_MODE_LEVELSET)
3033     HandleInfoScreen_LevelSet(button);
3034   else
3035     HandleInfoScreen_Main(mx, my, dx, dy, button);
3036
3037   DoAnimation();
3038 }
3039
3040
3041 /* ========================================================================= */
3042 /* type name functions                                                       */
3043 /* ========================================================================= */
3044
3045 void HandleTypeName(int newxpos, Key key)
3046 {
3047   static char last_player_name[MAX_PLAYER_NAME_LEN + 1];
3048   struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_NAME);
3049 #if 1
3050   struct MenuPosInfo *pos = mci->pos_input;
3051   int startx = mSX + ALIGNED_MENU_XPOS(pos);
3052   int starty = mSY + ALIGNED_MENU_YPOS(pos);
3053 #endif
3054 #if 1
3055   static int xpos = 0;
3056 #else
3057   static int xpos = 0, ypos = 2;
3058 #endif
3059   int font_nr = mci->font_input;
3060   int font_active_nr = FONT_ACTIVE(font_nr);
3061   int font_width = getFontWidth(font_active_nr);
3062 #if 1
3063 #if 0
3064   int startx = mSX + mci->pos_input->x;
3065   int starty = mSY + mci->pos_input->y;
3066 #endif
3067 #else
3068   int name_width = getFontWidth(FONT_MENU_1) * strlen("Name:");
3069   int startx = mSX + 32 + name_width;
3070   int starty = mSY + ypos * 32;
3071 #endif
3072   char key_char = getValidConfigValueChar(getCharFromKey(key));
3073   boolean is_valid_key_char = (key_char != 0 && (key_char != ' ' || xpos > 0));
3074   boolean is_active = TRUE;
3075
3076   DrawBackgroundForFont(startx,starty, pos->width, pos->height, font_active_nr);
3077
3078   if (newxpos)
3079   {
3080     strcpy(last_player_name, setup.player_name);
3081
3082     xpos = newxpos;
3083
3084 #if 0
3085     /* add one character width for added cursor character */
3086     pos->width += font_width;
3087     startx = mSX + ALIGNED_MENU_XPOS(pos);
3088
3089     DrawText(startx, starty, setup.player_name, font_active_nr);
3090     DrawText(startx + xpos * font_width, starty, "_", font_active_nr);
3091 #endif
3092   }
3093   else if (is_valid_key_char && xpos < MAX_PLAYER_NAME_LEN)
3094   {
3095     setup.player_name[xpos] = key_char;
3096     setup.player_name[xpos + 1] = 0;
3097
3098     xpos++;
3099
3100 #if 0
3101     /* add one character width for added name text character */
3102     pos->width += font_width;
3103     startx = mSX + ALIGNED_MENU_XPOS(pos);
3104
3105     DrawText(startx, starty, setup.player_name, font_active_nr);
3106     DrawText(startx + xpos * font_width, starty, "_", font_active_nr);
3107 #endif
3108   }
3109   else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
3110   {
3111     xpos--;
3112
3113     setup.player_name[xpos] = 0;
3114
3115 #if 0
3116     /* remove one character width for removed name text character */
3117     pos->width -= font_width;
3118     startx = mSX + ALIGNED_MENU_XPOS(pos);
3119
3120     DrawText(startx, starty, setup.player_name, font_active_nr);
3121     DrawText(startx + xpos * font_width, starty, "_ ", font_active_nr);
3122 #endif
3123   }
3124   else if (key == KSYM_Return && xpos > 0)
3125   {
3126 #if 0
3127     /* remove one character width for removed cursor text character */
3128     pos->width -= font_width;
3129     startx = mSX + ALIGNED_MENU_XPOS(pos);
3130
3131     DrawText(startx, starty, setup.player_name, font_nr);
3132     DrawText(startx + xpos * font_width, starty, " ", font_active_nr);
3133 #endif
3134
3135     SaveSetup();
3136
3137     is_active = FALSE;
3138
3139     game_status = GAME_MODE_MAIN;
3140   }
3141   else if (key == KSYM_Escape)
3142   {
3143     strcpy(setup.player_name, last_player_name);
3144
3145     is_active = FALSE;
3146
3147     game_status = GAME_MODE_MAIN;
3148   }
3149
3150   if (is_active)
3151   {
3152     pos->width = (strlen(setup.player_name) + 1) * font_width;
3153     startx = mSX + ALIGNED_MENU_XPOS(pos);
3154
3155     DrawText(startx, starty, setup.player_name, font_active_nr);
3156     DrawText(startx + xpos * font_width, starty, "_", font_active_nr);
3157   }
3158   else
3159   {
3160     pos->width = strlen(setup.player_name) * font_width;
3161     startx = mSX + ALIGNED_MENU_XPOS(pos);
3162
3163     DrawText(startx, starty, setup.player_name, font_nr);
3164   }
3165
3166   sprintf(main_input_name, "%s", setup.player_name);
3167 }
3168
3169
3170 /* ========================================================================= */
3171 /* tree menu functions                                                       */
3172 /* ========================================================================= */
3173
3174 static void DrawChooseTree(TreeInfo **ti_ptr)
3175 {
3176   UnmapAllGadgets();
3177
3178   FreeScreenGadgets();
3179   CreateScreenGadgets();
3180
3181   CloseDoor(DOOR_CLOSE_2);
3182
3183   ClearWindow();
3184
3185   HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
3186   MapScreenTreeGadgets(*ti_ptr);
3187
3188   FadeToFront();
3189   InitAnimation();
3190 }
3191
3192 static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti)
3193 {
3194   struct GadgetInfo *gi = screen_gadget[id];
3195   int items_max, items_visible, item_position;
3196
3197   items_max = numTreeInfoInGroup(ti);
3198   items_visible = NUM_MENU_ENTRIES_ON_SCREEN;
3199   item_position = first_entry;
3200
3201   if (item_position > items_max - items_visible)
3202     item_position = items_max - items_visible;
3203
3204   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
3205                GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
3206                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
3207 }
3208
3209 static void drawChooseTreeList(int first_entry, int num_page_entries,
3210                                TreeInfo *ti)
3211 {
3212   int i;
3213   char *title_string = NULL;
3214   int yoffset_sets = MENU_TITLE1_YPOS;
3215   int yoffset_setup = 16;
3216   int yoffset = (ti->type == TREE_TYPE_LEVEL_DIR ? yoffset_sets :
3217                  yoffset_setup);
3218   int last_game_status = game_status;   /* save current game status */
3219
3220   title_string = ti->infotext;
3221
3222   DrawTextSCentered(mSY - SY + yoffset, FONT_TITLE_1, title_string);
3223
3224   /* force LEVELS font on artwork setup screen */
3225   game_status = GAME_MODE_LEVELS;
3226
3227 #if 1
3228   /* clear tree list area, but not title or scrollbar */
3229   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
3230                  SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset,
3231                  NUM_MENU_ENTRIES_ON_SCREEN * 32);
3232 #else
3233   /* clear tree list area, but not title or scrollbar */
3234   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
3235                  SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset,
3236                  MAX_MENU_ENTRIES_ON_SCREEN * 32);
3237 #endif
3238
3239   for (i = 0; i < num_page_entries; i++)
3240   {
3241     TreeInfo *node, *node_first;
3242     int entry_pos = first_entry + i;
3243     int xpos = MENU_SCREEN_START_XPOS;
3244     int ypos = MENU_SCREEN_START_YPOS + i;
3245     int startx = mSX + xpos * 32;
3246     int starty = mSY + ypos * 32;
3247     int font_nr = FONT_TEXT_1;
3248     int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
3249     int startx_text = startx + font_xoffset;
3250     int startx_scrollbar = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
3251     int text_size = startx_scrollbar - startx_text;
3252     int max_buffer_len = text_size / getFontWidth(font_nr);
3253     char buffer[max_buffer_len + 1];
3254
3255     node_first = getTreeInfoFirstGroupEntry(ti);
3256     node = getTreeInfoFromPos(node_first, entry_pos);
3257
3258     strncpy(buffer, node->name, max_buffer_len);
3259     buffer[max_buffer_len] = '\0';
3260
3261     DrawText(startx, starty, buffer, font_nr + node->color);
3262
3263     if (node->parent_link)
3264       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
3265     else if (node->level_group)
3266       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
3267     else
3268       initCursor(i, IMG_MENU_BUTTON);
3269   }
3270
3271   game_status = last_game_status;       /* restore current game status */
3272
3273   redraw_mask |= REDRAW_FIELD;
3274 }
3275
3276 static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
3277 {
3278   TreeInfo *node, *node_first;
3279   int x, last_redraw_mask = redraw_mask;
3280   int ypos = MENU_TITLE2_YPOS;
3281   int font_nr = FONT_TITLE_2;
3282
3283   if (ti->type != TREE_TYPE_LEVEL_DIR)
3284     return;
3285
3286   node_first = getTreeInfoFirstGroupEntry(ti);
3287   node = getTreeInfoFromPos(node_first, entry_pos);
3288
3289   DrawBackgroundForFont(SX, SY + ypos, SXSIZE, getFontHeight(font_nr), font_nr);
3290
3291   if (node->parent_link)
3292     DrawTextFCentered(ypos, font_nr, "leave group \"%s\"",
3293                       node->class_desc);
3294   else if (node->level_group)
3295     DrawTextFCentered(ypos, font_nr, "enter group \"%s\"",
3296                       node->class_desc);
3297   else if (ti->type == TREE_TYPE_LEVEL_DIR)
3298     DrawTextFCentered(ypos, font_nr, "%3d levels (%s)",
3299                       node->levels, node->class_desc);
3300
3301   /* let BackToFront() redraw only what is needed */
3302   redraw_mask = last_redraw_mask | REDRAW_TILES;
3303   for (x = 0; x < SCR_FIELDX; x++)
3304     MarkTileDirty(x, 1);
3305 }
3306
3307 static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
3308                              TreeInfo **ti_ptr)
3309 {
3310   TreeInfo *ti = *ti_ptr;
3311   int x = 0;
3312   int y = ti->cl_cursor;
3313   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
3314   int num_entries = numTreeInfoInGroup(ti);
3315   int num_page_entries;
3316   int last_game_status = game_status;   /* save current game status */
3317   boolean position_set_by_scrollbar = (dx == 999);
3318
3319   /* force LEVELS draw offset on choose level and artwork setup screen */
3320   game_status = GAME_MODE_LEVELS;
3321
3322   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
3323     num_page_entries = num_entries;
3324   else
3325     num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
3326
3327   game_status = last_game_status;       /* restore current game status */
3328
3329   if (button == MB_MENU_INITIALIZE)
3330   {
3331     int num_entries = numTreeInfoInGroup(ti);
3332     int entry_pos = posTreeInfo(ti);
3333
3334     if (ti->cl_first == -1)
3335     {
3336       /* only on initialization */
3337       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
3338       ti->cl_cursor = entry_pos - ti->cl_first;
3339     }
3340     else if (ti->cl_cursor >= num_page_entries ||
3341              (num_entries > num_page_entries &&
3342               num_entries - ti->cl_first < num_page_entries))
3343     {
3344       /* only after change of list size (by custom graphic configuration) */
3345       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
3346       ti->cl_cursor = entry_pos - ti->cl_first;
3347     }
3348
3349     if (position_set_by_scrollbar)
3350       ti->cl_first = dy;
3351     else
3352       AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
3353                                 ti->cl_first, ti);
3354
3355     drawChooseTreeList(ti->cl_first, num_page_entries, ti);
3356     drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
3357     drawChooseTreeCursor(ti->cl_cursor, TRUE);
3358
3359     return;
3360   }
3361   else if (button == MB_MENU_LEAVE)
3362   {
3363     PlaySound(SND_MENU_ITEM_SELECTING);
3364
3365     if (ti->node_parent)
3366     {
3367       *ti_ptr = ti->node_parent;
3368       DrawChooseTree(ti_ptr);
3369     }
3370     else if (game_status == GAME_MODE_SETUP)
3371     {
3372       if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
3373         execSetupGame();
3374       else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
3375         execSetupGraphics();
3376       else
3377         execSetupArtwork();
3378     }
3379     else
3380     {
3381       game_status = GAME_MODE_MAIN;
3382       DrawMainMenu();
3383     }
3384
3385     return;
3386   }
3387
3388   if (mx || my)         /* mouse input */
3389   {
3390     int last_game_status = game_status; /* save current game status */
3391
3392     /* force LEVELS draw offset on artwork setup screen */
3393     game_status = GAME_MODE_LEVELS;
3394
3395     x = (mx - mSX) / 32;
3396     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3397
3398     game_status = last_game_status;     /* restore current game status */
3399   }
3400   else if (dx || dy)    /* keyboard or scrollbar/scrollbutton input */
3401   {
3402     /* move cursor instead of scrolling when already at start/end of list */
3403     if (dy == -1 * SCROLL_LINE && ti->cl_first == 0)
3404       dy = -1;
3405     else if (dy == +1 * SCROLL_LINE &&
3406              ti->cl_first + num_page_entries == num_entries)
3407       dy = 1;
3408
3409     /* handle scrolling screen one line or page */
3410     if (ti->cl_cursor + dy < 0 ||
3411         ti->cl_cursor + dy > num_page_entries - 1)
3412     {
3413       if (ABS(dy) == SCROLL_PAGE)
3414         step = num_page_entries - 1;
3415
3416       if (dy < 0 && ti->cl_first > 0)
3417       {
3418         /* scroll page/line up */
3419
3420         ti->cl_first -= step;
3421         if (ti->cl_first < 0)
3422           ti->cl_first = 0;
3423
3424         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
3425         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
3426         drawChooseTreeCursor(ti->cl_cursor, TRUE);
3427
3428         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
3429                                   ti->cl_first, ti);
3430       }
3431       else if (dy > 0 && ti->cl_first + num_page_entries < num_entries)
3432       {
3433         /* scroll page/line down */
3434
3435         ti->cl_first += step;
3436         if (ti->cl_first + num_page_entries > num_entries)
3437           ti->cl_first = MAX(0, num_entries - num_page_entries);
3438
3439         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
3440         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
3441         drawChooseTreeCursor(ti->cl_cursor, TRUE);
3442
3443         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
3444                                   ti->cl_first, ti);
3445       }
3446
3447       return;
3448     }
3449
3450     /* handle moving cursor one line */
3451     y = ti->cl_cursor + dy;
3452   }
3453
3454   if (dx == 1)
3455   {
3456     TreeInfo *node_first, *node_cursor;
3457     int entry_pos = ti->cl_first + y;
3458
3459     node_first = getTreeInfoFirstGroupEntry(ti);
3460     node_cursor = getTreeInfoFromPos(node_first, entry_pos);
3461
3462     if (node_cursor->node_group)
3463     {
3464       PlaySound(SND_MENU_ITEM_SELECTING);
3465
3466       node_cursor->cl_first = ti->cl_first;
3467       node_cursor->cl_cursor = ti->cl_cursor;
3468       *ti_ptr = node_cursor->node_group;
3469       DrawChooseTree(ti_ptr);
3470
3471       return;
3472     }
3473   }
3474   else if (dx == -1 && ti->node_parent)
3475   {
3476     PlaySound(SND_MENU_ITEM_SELECTING);
3477
3478     *ti_ptr = ti->node_parent;
3479     DrawChooseTree(ti_ptr);
3480
3481     return;
3482   }
3483
3484   if (!anyScrollbarGadgetActive() &&
3485       IN_VIS_FIELD(x, y) &&
3486       mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
3487       y >= 0 && y < num_page_entries)
3488   {
3489     if (button)
3490     {
3491       if (y != ti->cl_cursor)
3492       {
3493         PlaySound(SND_MENU_ITEM_ACTIVATING);
3494
3495         drawChooseTreeCursor(ti->cl_cursor, FALSE);
3496         drawChooseTreeCursor(y, TRUE);
3497         drawChooseTreeInfo(ti->cl_first + y, ti);
3498
3499         ti->cl_cursor = y;
3500       }
3501     }
3502     else
3503     {
3504       TreeInfo *node_first, *node_cursor;
3505       int entry_pos = ti->cl_first + y;
3506
3507       PlaySound(SND_MENU_ITEM_SELECTING);
3508
3509       node_first = getTreeInfoFirstGroupEntry(ti);
3510       node_cursor = getTreeInfoFromPos(node_first, entry_pos);
3511
3512       if (node_cursor->node_group)
3513       {
3514         node_cursor->cl_first = ti->cl_first;
3515         node_cursor->cl_cursor = ti->cl_cursor;
3516         *ti_ptr = node_cursor->node_group;
3517         DrawChooseTree(ti_ptr);
3518       }
3519       else if (node_cursor->parent_link)
3520       {
3521         *ti_ptr = node_cursor->node_parent;
3522         DrawChooseTree(ti_ptr);
3523       }
3524       else
3525       {
3526         node_cursor->cl_first = ti->cl_first;
3527         node_cursor->cl_cursor = ti->cl_cursor;
3528         *ti_ptr = node_cursor;
3529
3530         if (ti->type == TREE_TYPE_LEVEL_DIR)
3531         {
3532           LoadLevelSetup_SeriesInfo();
3533
3534           SaveLevelSetup_LastSeries();
3535           SaveLevelSetup_SeriesInfo();
3536           TapeErase();
3537         }
3538
3539         if (game_status == GAME_MODE_SETUP)
3540         {
3541           if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
3542             execSetupGame();
3543           else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
3544             execSetupGraphics();
3545           else
3546             execSetupArtwork();
3547         }
3548         else
3549         {
3550           game_status = GAME_MODE_MAIN;
3551           DrawMainMenu();
3552         }
3553       }
3554     }
3555   }
3556 }
3557
3558 void DrawChooseLevel()
3559 {
3560   SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
3561
3562   DrawChooseTree(&leveldir_current);
3563
3564   PlayMenuSound();
3565   PlayMenuMusic();
3566 }
3567
3568 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
3569 {
3570   HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
3571
3572   DoAnimation();
3573 }
3574
3575 void DrawHallOfFame(int highlight_position)
3576 {
3577   UnmapAllGadgets();
3578   FadeSoundsAndMusic();
3579
3580   /* (this is needed when called from GameEnd() after winning a game) */
3581   KeyboardAutoRepeatOn();
3582   ActivateJoystick();
3583
3584   /* (this is needed when called from GameEnd() after winning a game) */
3585   SetDrawDeactivationMask(REDRAW_NONE);
3586   SetDrawBackgroundMask(REDRAW_FIELD);
3587
3588   CloseDoor(DOOR_CLOSE_2);
3589
3590   if (highlight_position < 0) 
3591     LoadScore(level_nr);
3592
3593   FadeOut(REDRAW_FIELD);
3594
3595   InitAnimation();
3596
3597   PlayMenuSound();
3598   PlayMenuMusic();
3599
3600   HandleHallOfFame(highlight_position, 0, 0, 0, MB_MENU_INITIALIZE);
3601
3602   FadeIn(REDRAW_FIELD);
3603 }
3604
3605 static void drawHallOfFameList(int first_entry, int highlight_position)
3606 {
3607   int i;
3608
3609   SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
3610   ClearWindow();
3611
3612   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame");
3613   DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
3614                     "HighScores of Level %d", level_nr);
3615
3616   for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
3617   {
3618     int entry = first_entry + i;
3619     boolean active = (entry == highlight_position);
3620     int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
3621     int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
3622     int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
3623     int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
3624     int dx1 = 3 * getFontWidth(font_nr1);
3625     int dx2 = dx1 + getFontWidth(font_nr1);
3626     int dx3 = dx2 + 25 * getFontWidth(font_nr3);
3627     int sy = mSY + 64 + i * 32;
3628
3629     DrawText(mSX, sy, int2str(entry + 1, 3), font_nr1);
3630     DrawText(mSX + dx1, sy, ".", font_nr1);
3631     DrawText(mSX + dx2, sy, ".........................", font_nr3);
3632
3633     if (!strEqual(highscore[entry].Name, EMPTY_PLAYER_NAME))
3634       DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
3635
3636     DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
3637   }
3638
3639   redraw_mask |= REDRAW_FIELD;
3640 }
3641
3642 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
3643 {
3644   static int first_entry = 0;
3645   static int highlight_position = 0;
3646   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
3647
3648   if (button == MB_MENU_INITIALIZE)
3649   {
3650     first_entry = 0;
3651     highlight_position = mx;
3652     drawHallOfFameList(first_entry, highlight_position);
3653
3654     return;
3655   }
3656
3657   if (ABS(dy) == SCROLL_PAGE)           /* handle scrolling one page */
3658     step = NUM_MENU_ENTRIES_ON_SCREEN - 1;
3659
3660   if (dy < 0)
3661   {
3662     if (first_entry > 0)
3663     {
3664       first_entry -= step;
3665       if (first_entry < 0)
3666         first_entry = 0;
3667
3668       drawHallOfFameList(first_entry, highlight_position);
3669     }
3670   }
3671   else if (dy > 0)
3672   {
3673     if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
3674     {
3675       first_entry += step;
3676       if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
3677         first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
3678
3679       drawHallOfFameList(first_entry, highlight_position);
3680     }
3681   }
3682   else if (button == MB_MENU_LEAVE)
3683   {
3684     PlaySound(SND_MENU_ITEM_SELECTING);
3685
3686     FadeSound(SND_BACKGROUND_SCORES);
3687
3688     game_status = GAME_MODE_MAIN;
3689
3690     DrawMainMenu();
3691   }
3692   else if (button == MB_MENU_CHOICE)
3693   {
3694     PlaySound(SND_MENU_ITEM_SELECTING);
3695
3696     FadeSound(SND_BACKGROUND_SCORES);
3697     FadeOut(REDRAW_FIELD);
3698
3699     game_status = GAME_MODE_MAIN;
3700
3701     DrawAndFadeInMainMenu(REDRAW_FIELD);
3702   }
3703
3704   if (game_status == GAME_MODE_SCORES)
3705     PlayMenuSoundIfLoop();
3706
3707   DoAnimation();
3708 }
3709
3710
3711 /* ========================================================================= */
3712 /* setup screen functions                                                    */
3713 /* ========================================================================= */
3714
3715 static struct TokenInfo *setup_info;
3716 static int num_setup_info;
3717
3718 static char *screen_mode_text;
3719 static char *game_speed_text;
3720 static char *graphics_set_name;
3721 static char *sounds_set_name;
3722 static char *music_set_name;
3723
3724 static void execSetupMain()
3725 {
3726   setup_mode = SETUP_MODE_MAIN;
3727   DrawSetupScreen();
3728 }
3729
3730 static void execSetupGame()
3731 {
3732   if (game_speeds == NULL)
3733   {
3734     int i;
3735
3736     for (i = 0; game_speeds_list[i].value != -1; i++)
3737     {
3738       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
3739       char identifier[32], name[32];
3740       int value = game_speeds_list[i].value;
3741       char *text = game_speeds_list[i].text;
3742
3743       ti->node_top = &game_speeds;
3744       ti->sort_priority = 10000 - value;
3745
3746       sprintf(identifier, "%d", value);
3747       sprintf(name, "%s", text);
3748
3749       setString(&ti->identifier, identifier);
3750       setString(&ti->name, name);
3751       setString(&ti->name_sorting, name);
3752       setString(&ti->infotext, "Game Speed");
3753
3754       pushTreeInfo(&game_speeds, ti);
3755     }
3756
3757     /* sort game speed values to start with slowest game speed */
3758     sortTreeInfo(&game_speeds);
3759
3760     /* set current game speed to configured game speed value */
3761     game_speed_current =
3762       getTreeInfoFromIdentifier(game_speeds, i_to_a(setup.game_frame_delay));
3763
3764     /* if that fails, set current game speed to reliable default value */
3765     if (game_speed_current == NULL)
3766       game_speed_current =
3767         getTreeInfoFromIdentifier(game_speeds, i_to_a(GAME_FRAME_DELAY));
3768
3769     /* if that also fails, set current game speed to first available speed */
3770     if (game_speed_current == NULL)
3771       game_speed_current = game_speeds;
3772   }
3773
3774   setup.game_frame_delay = atoi(game_speed_current->identifier);
3775
3776   /* needed for displaying game speed text instead of identifier */
3777   game_speed_text = game_speed_current->name;
3778
3779   setup_mode = SETUP_MODE_GAME;
3780   DrawSetupScreen();
3781 }
3782
3783 static void execSetupChooseGameSpeed()
3784 {
3785   setup_mode = SETUP_MODE_CHOOSE_GAME_SPEED;
3786   DrawSetupScreen();
3787 }
3788
3789 static void execSetupEditor()
3790 {
3791   setup_mode = SETUP_MODE_EDITOR;
3792   DrawSetupScreen();
3793 }
3794
3795 static void execSetupGraphics()
3796 {
3797   if (video.fullscreen_available && screen_modes == NULL)
3798   {
3799     int i;
3800
3801     for (i = 0; video.fullscreen_modes[i].width != -1; i++)
3802     {
3803       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
3804       char identifier[32], name[32];
3805       int x = video.fullscreen_modes[i].width;
3806       int y = video.fullscreen_modes[i].height;
3807       int xx, yy;
3808
3809       get_aspect_ratio_from_screen_mode(&video.fullscreen_modes[i], &xx, &yy);
3810
3811       ti->node_top = &screen_modes;
3812       ti->sort_priority = x * 10000 + y;
3813
3814       sprintf(identifier, "%dx%d", x, y);
3815       sprintf(name, "%d x %d [%d:%d]", x, y, xx, yy);
3816
3817       setString(&ti->identifier, identifier);
3818       setString(&ti->name, name);
3819       setString(&ti->name_sorting, name);
3820       setString(&ti->infotext, "Fullscreen Mode");
3821
3822       pushTreeInfo(&screen_modes, ti);
3823     }
3824
3825     /* sort fullscreen modes to start with lowest available screen resolution */
3826     sortTreeInfo(&screen_modes);
3827
3828     /* set current screen mode for fullscreen mode to configured setup value */
3829     screen_mode_current = getTreeInfoFromIdentifier(screen_modes,
3830                                                     setup.fullscreen_mode);
3831
3832     /* if that fails, set current screen mode to reliable default value */
3833     if (screen_mode_current == NULL)
3834       screen_mode_current = getTreeInfoFromIdentifier(screen_modes,
3835                                                       DEFAULT_FULLSCREEN_MODE);
3836
3837     /* if that also fails, set current screen mode to first available mode */
3838     if (screen_mode_current == NULL)
3839       screen_mode_current = screen_modes;
3840
3841     if (screen_mode_current == NULL)
3842       video.fullscreen_available = FALSE;
3843   }
3844
3845   if (video.fullscreen_available)
3846   {
3847     setup.fullscreen_mode = screen_mode_current->identifier;
3848
3849     /* needed for displaying screen mode name instead of identifier */
3850     screen_mode_text = screen_mode_current->name;
3851   }
3852
3853   setup_mode = SETUP_MODE_GRAPHICS;
3854   DrawSetupScreen();
3855 }
3856
3857 static void execSetupChooseScreenMode()
3858 {
3859   if (!video.fullscreen_available)
3860     return;
3861
3862   setup_mode = SETUP_MODE_CHOOSE_SCREEN_MODE;
3863   DrawSetupScreen();
3864 }
3865
3866 static void execSetupSound()
3867 {
3868   setup_mode = SETUP_MODE_SOUND;
3869   DrawSetupScreen();
3870 }
3871
3872 static void execSetupArtwork()
3873 {
3874   setup.graphics_set = artwork.gfx_current->identifier;
3875   setup.sounds_set = artwork.snd_current->identifier;
3876   setup.music_set = artwork.mus_current->identifier;
3877
3878   /* needed if last screen (setup choice) changed graphics, sounds or music */
3879   ReloadCustomArtwork(0);
3880
3881   /* needed for displaying artwork name instead of artwork identifier */
3882   graphics_set_name = artwork.gfx_current->name;
3883   sounds_set_name = artwork.snd_current->name;
3884   music_set_name = artwork.mus_current->name;
3885
3886   setup_mode = SETUP_MODE_ARTWORK;
3887   DrawSetupScreen();
3888 }
3889
3890 static void execSetupChooseGraphics()
3891 {
3892   setup_mode = SETUP_MODE_CHOOSE_GRAPHICS;
3893   DrawSetupScreen();
3894 }
3895
3896 static void execSetupChooseSounds()
3897 {
3898   setup_mode = SETUP_MODE_CHOOSE_SOUNDS;
3899   DrawSetupScreen();
3900 }
3901
3902 static void execSetupChooseMusic()
3903 {
3904   setup_mode = SETUP_MODE_CHOOSE_MUSIC;
3905   DrawSetupScreen();
3906 }
3907
3908 static void execSetupInput()
3909 {
3910   setup_mode = SETUP_MODE_INPUT;
3911   DrawSetupScreen();
3912 }
3913
3914 static void execSetupShortcut1()
3915 {
3916   setup_mode = SETUP_MODE_SHORTCUT_1;
3917   DrawSetupScreen();
3918 }
3919
3920 static void execSetupShortcut2()
3921 {
3922   setup_mode = SETUP_MODE_SHORTCUT_2;
3923   DrawSetupScreen();
3924 }
3925
3926 static void execExitSetup()
3927 {
3928   game_status = GAME_MODE_MAIN;
3929   DrawMainMenu();
3930 }
3931
3932 static void execSaveAndExitSetup()
3933 {
3934   SaveSetup();
3935   execExitSetup();
3936 }
3937
3938 static struct TokenInfo setup_info_main[] =
3939 {
3940   { TYPE_ENTER_MENU,    execSetupGame,          "Game & Menu"           },
3941   { TYPE_ENTER_MENU,    execSetupEditor,        "Editor"                },
3942   { TYPE_ENTER_MENU,    execSetupGraphics,      "Graphics"              },
3943   { TYPE_ENTER_MENU,    execSetupSound,         "Sound & Music"         },
3944   { TYPE_ENTER_MENU,    execSetupArtwork,       "Custom Artwork"        },
3945   { TYPE_ENTER_MENU,    execSetupInput,         "Input Devices"         },
3946   { TYPE_ENTER_MENU,    execSetupShortcut1,     "Key Shortcuts 1"       },
3947   { TYPE_ENTER_MENU,    execSetupShortcut2,     "Key Shortcuts 2"       },
3948   { TYPE_EMPTY,         NULL,                   ""                      },
3949   { TYPE_LEAVE_MENU,    execExitSetup,          "Exit"                  },
3950   { TYPE_LEAVE_MENU,    execSaveAndExitSetup,   "Save and Exit"         },
3951
3952   { 0,                  NULL,                   NULL                    }
3953 };
3954
3955 static struct TokenInfo setup_info_game[] =
3956 {
3957   { TYPE_SWITCH,        &setup.team_mode,       "Team-Mode (Multi-Player):" },
3958   { TYPE_YES_NO,        &setup.input_on_focus,  "Only Move Focussed Player:" },
3959   { TYPE_SWITCH,        &setup.handicap,        "Handicap:"             },
3960   { TYPE_SWITCH,        &setup.skip_levels,     "Skip Unsolved Levels:" },
3961   { TYPE_SWITCH,        &setup.time_limit,      "Time Limit:"           },
3962   { TYPE_SWITCH,        &setup.autorecord,      "Auto-Record Tapes:"    },
3963   { TYPE_ENTER_LIST,    execSetupChooseGameSpeed, "Game Speed:"         },
3964   { TYPE_STRING,        &game_speed_text,       ""                      },
3965   { TYPE_EMPTY,         NULL,                   ""                      },
3966   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3967
3968   { 0,                  NULL,                   NULL                    }
3969 };
3970
3971 static struct TokenInfo setup_info_editor[] =
3972 {
3973 #if 0
3974   { TYPE_SWITCH,        &setup.editor.el_boulderdash,   "Boulder Dash:" },
3975   { TYPE_SWITCH,        &setup.editor.el_emerald_mine,  "Emerald Mine:" },
3976   { TYPE_SWITCH, &setup.editor.el_emerald_mine_club,    "Emerald Mine Club:" },
3977   { TYPE_SWITCH,        &setup.editor.el_more,          "Rocks'n'Diamonds:" },
3978   { TYPE_SWITCH,        &setup.editor.el_sokoban,       "Sokoban:"      },
3979   { TYPE_SWITCH,        &setup.editor.el_supaplex,      "Supaplex:"     },
3980   { TYPE_SWITCH,        &setup.editor.el_diamond_caves, "Diamond Caves II:" },
3981   { TYPE_SWITCH,        &setup.editor.el_dx_boulderdash,"DX-Boulderdash:" },
3982 #endif
3983   { TYPE_SWITCH,        &setup.editor.el_chars,         "Text Characters:" },
3984   { TYPE_SWITCH, &setup.editor.el_steel_chars, "Text Characters (Steel):" },
3985   { TYPE_SWITCH,        &setup.editor.el_custom,  "Custom & Group Elements:" },
3986 #if 0
3987   { TYPE_SWITCH,        &setup.editor.el_headlines,     "Headlines:"    },
3988 #endif
3989   { TYPE_SWITCH, &setup.editor.el_user_defined, "User defined element list:" },
3990   { TYPE_SWITCH,        &setup.editor.el_dynamic,  "Dynamic level elements:" },
3991   { TYPE_EMPTY,         NULL,                   ""                      },
3992 #if 0
3993   { TYPE_SWITCH,        &setup.editor.el_by_game,   "Show elements by game:" },
3994   { TYPE_SWITCH,        &setup.editor.el_by_type,   "Show elements by type:" },
3995   { TYPE_EMPTY,         NULL,                   ""                      },
3996 #endif
3997   { TYPE_SWITCH, &setup.editor.show_element_token,      "Show element token:" },
3998   { TYPE_EMPTY,         NULL,                   ""                      },
3999   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
4000
4001   { 0,                  NULL,                   NULL                    }
4002 };
4003
4004 static struct TokenInfo setup_info_graphics[] =
4005 {
4006   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen:"           },
4007   { TYPE_ENTER_LIST,    execSetupChooseScreenMode, "Fullscreen Mode:"   },
4008   { TYPE_STRING,        &screen_mode_text,      ""                      },
4009   { TYPE_SWITCH,        &setup.scroll_delay,    "Delayed Scrolling:"    },
4010 #if 0
4011   { TYPE_SWITCH,        &setup.soft_scrolling,  "Soft Scrolling:"       },
4012   { TYPE_SWITCH,        &setup.double_buffering,"Double-Buffering:"     },
4013 #endif
4014   { TYPE_SWITCH,        &setup.fade_screens,    "Fade Screens:"         },
4015   { TYPE_SWITCH,        &setup.quick_switch,    "Quick Player Focus Switch:" },
4016   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Menu Doors:"     },
4017   { TYPE_SWITCH,        &setup.show_titlescreen,"Show Title Screens:"   },
4018   { TYPE_SWITCH,        &setup.toons,           "Show Toons:"           },
4019   { TYPE_ECS_AGA,       &setup.prefer_aga_graphics,"EMC graphics preference:" },
4020   { TYPE_EMPTY,         NULL,                   ""                      },
4021   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
4022
4023   { 0,                  NULL,                   NULL                    }
4024 };
4025
4026 static struct TokenInfo setup_info_sound[] =
4027 {
4028   { TYPE_SWITCH,        &setup.sound_simple,    "Sound Effects (Normal):"  },
4029   { TYPE_SWITCH,        &setup.sound_loops,     "Sound Effects (Looping):" },
4030   { TYPE_SWITCH,        &setup.sound_music,     "Music:"                },
4031   { TYPE_EMPTY,         NULL,                   ""                      },
4032   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
4033
4034   { 0,                  NULL,                   NULL                    }
4035 };
4036
4037 static struct TokenInfo setup_info_artwork[] =
4038 {
4039   { TYPE_ENTER_LIST,    execSetupChooseGraphics,"Custom Graphics:"      },
4040   { TYPE_STRING,        &graphics_set_name,     ""                      },
4041   { TYPE_ENTER_LIST,    execSetupChooseSounds,  "Custom Sounds:"        },
4042   { TYPE_STRING,        &sounds_set_name,       ""                      },
4043   { TYPE_ENTER_LIST,    execSetupChooseMusic,   "Custom Music:"         },
4044   { TYPE_STRING,        &music_set_name,        ""                      },
4045   { TYPE_EMPTY,         NULL,                   ""                      },
4046 #if 1
4047   { TYPE_YES_NO, &setup.override_level_graphics,"Override Level Graphics:" },
4048   { TYPE_YES_NO, &setup.override_level_sounds,  "Override Level Sounds:"   },
4049   { TYPE_YES_NO, &setup.override_level_music,   "Override Level Music:"    },
4050 #else
4051   { TYPE_STRING,        NULL,                   "Override Level Artwork:"},
4052   { TYPE_YES_NO,        &setup.override_level_graphics, "Graphics:"     },
4053   { TYPE_YES_NO,        &setup.override_level_sounds,   "Sounds:"       },
4054   { TYPE_YES_NO,        &setup.override_level_music,    "Music:"        },
4055 #endif
4056   { TYPE_EMPTY,         NULL,                   ""                      },
4057   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
4058
4059   { 0,                  NULL,                   NULL                    }
4060 };
4061
4062 static struct TokenInfo setup_info_input[] =
4063 {
4064   { TYPE_SWITCH,        NULL,                   "Player:"               },
4065   { TYPE_SWITCH,        NULL,                   "Device:"               },
4066   { TYPE_ENTER_MENU,    NULL,                   ""                      },
4067   { TYPE_EMPTY,         NULL,                   ""                      },
4068   { TYPE_EMPTY,         NULL,                   ""                      },
4069   { TYPE_EMPTY,         NULL,                   ""                      },
4070   { TYPE_EMPTY,         NULL,                   ""                      },
4071   { TYPE_EMPTY,         NULL,                   ""                      },
4072   { TYPE_EMPTY,         NULL,                   ""                      },
4073   { TYPE_EMPTY,         NULL,                   ""                      },
4074   { TYPE_EMPTY,         NULL,                   ""                      },
4075   { TYPE_EMPTY,         NULL,                   ""                      },
4076   { TYPE_EMPTY,         NULL,                   ""                      },
4077   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
4078
4079   { 0,                  NULL,                   NULL                    }
4080 };
4081
4082 static struct TokenInfo setup_info_shortcut_1[] =
4083 {
4084   { TYPE_KEYTEXT,       NULL,           "Quick Save Game to Tape:",     },
4085   { TYPE_KEY,           &setup.shortcut.save_game, ""                   },
4086   { TYPE_KEYTEXT,       NULL,           "Quick Load Game from Tape:",   },
4087   { TYPE_KEY,           &setup.shortcut.load_game, ""                   },
4088   { TYPE_KEYTEXT,       NULL,           "Start Game & Toggle Pause:",   },
4089   { TYPE_KEY,           &setup.shortcut.toggle_pause, ""                },
4090   { TYPE_EMPTY,         NULL,                   ""                      },
4091   { TYPE_YES_NO,        &setup.ask_on_escape,   "Ask on 'Esc' Key:"     },
4092   { TYPE_YES_NO, &setup.ask_on_escape_editor,   "Ask on 'Esc' Key (Editor):" },
4093   { TYPE_EMPTY,         NULL,                   ""                      },
4094   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
4095
4096   { 0,                  NULL,                   NULL                    }
4097 };
4098
4099 static struct TokenInfo setup_info_shortcut_2[] =
4100 {
4101   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 1:",       },
4102   { TYPE_KEY,           &setup.shortcut.focus_player[0], ""             },
4103   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 2:",       },
4104   { TYPE_KEY,           &setup.shortcut.focus_player[1], ""             },
4105   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 3:",       },
4106   { TYPE_KEY,           &setup.shortcut.focus_player[2], ""             },
4107   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 4:",       },
4108   { TYPE_KEY,           &setup.shortcut.focus_player[3], ""             },
4109   { TYPE_KEYTEXT,       NULL,           "Set Focus to All Players:",    },
4110   { TYPE_KEY,           &setup.shortcut.focus_player_all, ""            },
4111   { TYPE_EMPTY,         NULL,                   ""                      },
4112   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
4113
4114   { 0,                  NULL,                   NULL                    }
4115 };
4116
4117 static Key getSetupKey()
4118 {
4119   Key key = KSYM_UNDEFINED;
4120   boolean got_key_event = FALSE;
4121
4122   while (!got_key_event)
4123   {
4124     if (PendingEvent())         /* got event */
4125     {
4126       Event event;
4127
4128       NextEvent(&event);
4129
4130       switch (event.type)
4131       {
4132         case EVENT_KEYPRESS:
4133           {
4134             key = GetEventKey((KeyEvent *)&event, TRUE);
4135
4136             /* press 'Escape' or 'Enter' to keep the existing key binding */
4137             if (key == KSYM_Escape || key == KSYM_Return)
4138               key = KSYM_UNDEFINED;     /* keep old value */
4139
4140             got_key_event = TRUE;
4141           }
4142           break;
4143
4144         case EVENT_KEYRELEASE:
4145           key_joystick_mapping = 0;
4146           break;
4147
4148         default:
4149           HandleOtherEvents(&event);
4150           break;
4151       }
4152     }
4153
4154     DoAnimation();
4155     BackToFront();
4156
4157     /* don't eat all CPU time */
4158     Delay(10);
4159   }
4160
4161   return key;
4162 }
4163
4164 static int getSetupTextFont(int type)
4165 {
4166   if (type & (TYPE_SWITCH |
4167               TYPE_YES_NO |
4168               TYPE_STRING |
4169               TYPE_ECS_AGA |
4170               TYPE_KEYTEXT |
4171               TYPE_ENTER_LIST))
4172     return FONT_MENU_2;
4173   else
4174     return FONT_MENU_1;
4175 }
4176
4177 static int getSetupValueFont(int type, void *value)
4178 {
4179   if (type & TYPE_KEY)
4180     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
4181   else if (type & TYPE_STRING)
4182     return FONT_VALUE_2;
4183   else if (type & TYPE_ECS_AGA)
4184     return FONT_VALUE_1;
4185   else if (type & TYPE_BOOLEAN_STYLE)
4186     return (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
4187   else
4188     return FONT_VALUE_1;
4189 }
4190
4191 static void drawSetupValue(int pos)
4192 {
4193   boolean font_draw_xoffset_modified = FALSE;
4194   int font_draw_xoffset_old = -1;
4195   int xpos = MENU_SCREEN_VALUE_XPOS;
4196   int ypos = MENU_SCREEN_START_YPOS + pos;
4197   int startx = mSX + xpos * 32;
4198   int starty = mSY + ypos * 32;
4199   int font_nr, font_width;
4200   int type = setup_info[pos].type;
4201   void *value = setup_info[pos].value;
4202   char *value_string = getSetupValue(type, value);
4203   int i;
4204
4205   if (value_string == NULL)
4206     return;
4207
4208   if (type & TYPE_KEY)
4209   {
4210     xpos = MENU_SCREEN_START_XPOS;
4211
4212     if (type & TYPE_QUERY)
4213     {
4214       value_string = "<press key>";
4215     }
4216   }
4217   else if (type & TYPE_STRING)
4218   {
4219     int max_value_len = (SCR_FIELDX - 2) * 2;
4220
4221     xpos = MENU_SCREEN_START_XPOS;
4222
4223     if (strlen(value_string) > max_value_len)
4224       value_string[max_value_len] = '\0';
4225   }
4226
4227   startx = mSX + xpos * 32;
4228   starty = mSY + ypos * 32;
4229   font_nr = getSetupValueFont(type, value);
4230   font_width = getFontWidth(font_nr);
4231
4232   /* downward compatibility correction for Juergen Bonhagen's menu settings */
4233   if (setup_mode != SETUP_MODE_INPUT)
4234   {
4235     int check_font_nr = FONT_OPTION_ON; /* known font that needs correction */
4236     int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
4237     int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset;
4238     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
4239     int text_font_nr = getSetupTextFont(FONT_MENU_2);
4240     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
4241     int text_width = MAX_MENU_TEXT_LENGTH_MEDIUM * getFontWidth(text_font_nr);
4242     boolean correct_font_draw_xoffset = FALSE;
4243
4244     if (xpos == MENU_SCREEN_START_XPOS &&
4245         startx + font1_xoffset < text_startx + text_font_xoffset)
4246       correct_font_draw_xoffset = TRUE;
4247
4248     if (xpos == MENU_SCREEN_VALUE_XPOS &&
4249         startx + font2_xoffset < text_startx + text_width + text_font_xoffset)
4250       correct_font_draw_xoffset = TRUE;
4251
4252     /* check if setup value would overlap with setup text when printed */
4253     /* (this can happen for extreme/wrong values for font draw offset) */
4254     if (correct_font_draw_xoffset)
4255     {
4256       font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset;
4257       font_draw_xoffset_modified = TRUE;
4258
4259       if (type & TYPE_KEY)
4260         getFontBitmapInfo(font_nr)->draw_xoffset += 2 * getFontWidth(font_nr);
4261       else if (!(type & TYPE_STRING))
4262         getFontBitmapInfo(font_nr)->draw_xoffset = text_font_xoffset + 20 -
4263           MAX_MENU_TEXT_LENGTH_MEDIUM * (16 - getFontWidth(text_font_nr));
4264     }
4265   }
4266
4267   for (i = 0; i <= MENU_SCREEN_MAX_XPOS - xpos; i++)
4268     DrawText(startx + i * font_width, starty, " ", font_nr);
4269
4270   DrawText(startx, starty, value_string, font_nr);
4271
4272   if (font_draw_xoffset_modified)
4273     getFontBitmapInfo(font_nr)->draw_xoffset = font_draw_xoffset_old;
4274 }
4275
4276 static void changeSetupValue(int pos)
4277 {
4278   if (setup_info[pos].type & TYPE_BOOLEAN_STYLE)
4279   {
4280     *(boolean *)setup_info[pos].value ^= TRUE;
4281   }
4282   else if (setup_info[pos].type & TYPE_KEY)
4283   {
4284     Key key;
4285
4286     setup_info[pos].type |= TYPE_QUERY;
4287     drawSetupValue(pos);
4288     setup_info[pos].type &= ~TYPE_QUERY;
4289
4290     key = getSetupKey();
4291     if (key != KSYM_UNDEFINED)
4292       *(Key *)setup_info[pos].value = key;
4293   }
4294
4295   drawSetupValue(pos);
4296 }
4297
4298 static void DrawCursorAndText_Setup(int pos, boolean active)
4299 {
4300   int xpos = MENU_SCREEN_START_XPOS;
4301   int ypos = MENU_SCREEN_START_YPOS + pos;
4302   int font_nr = getSetupTextFont(setup_info[pos].type);
4303
4304   if (setup_info == setup_info_input)
4305     font_nr = FONT_MENU_1;
4306
4307   if (active)
4308     font_nr = FONT_ACTIVE(font_nr);
4309
4310   DrawText(mSX + xpos * 32, mSY + ypos * 32, setup_info[pos].text, font_nr);
4311
4312   if (setup_info[pos].type & ~TYPE_SKIP_ENTRY)
4313     drawCursor(pos, active);
4314 }
4315
4316 static void DrawSetupScreen_Generic()
4317 {
4318   char *title_string = NULL;
4319   int i;
4320
4321   UnmapAllGadgets();
4322   CloseDoor(DOOR_CLOSE_2);
4323
4324   ClearWindow();
4325
4326   if (setup_mode == SETUP_MODE_MAIN)
4327   {
4328     setup_info = setup_info_main;
4329     title_string = "Setup";
4330   }
4331   else if (setup_mode == SETUP_MODE_GAME)
4332   {
4333     setup_info = setup_info_game;
4334     title_string = "Setup Game";
4335   }
4336   else if (setup_mode == SETUP_MODE_EDITOR)
4337   {
4338     setup_info = setup_info_editor;
4339     title_string = "Setup Editor";
4340   }
4341   else if (setup_mode == SETUP_MODE_GRAPHICS)
4342   {
4343     setup_info = setup_info_graphics;
4344     title_string = "Setup Graphics";
4345   }
4346   else if (setup_mode == SETUP_MODE_SOUND)
4347   {
4348     setup_info = setup_info_sound;
4349     title_string = "Setup Sound";
4350   }
4351   else if (setup_mode == SETUP_MODE_ARTWORK)
4352   {
4353     setup_info = setup_info_artwork;
4354     title_string = "Custom Artwork";
4355   }
4356   else if (setup_mode == SETUP_MODE_SHORTCUT_1)
4357   {
4358     setup_info = setup_info_shortcut_1;
4359     title_string = "Setup Shortcuts";
4360   }
4361   else if (setup_mode == SETUP_MODE_SHORTCUT_2)
4362   {
4363     setup_info = setup_info_shortcut_2;
4364     title_string = "Setup Shortcuts";
4365   }
4366
4367   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
4368
4369   num_setup_info = 0;
4370   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
4371   {
4372     void *value_ptr = setup_info[i].value;
4373 #if 1
4374 #else
4375     int xpos = MENU_SCREEN_START_XPOS;
4376     int ypos = MENU_SCREEN_START_YPOS + i;
4377     int font_nr;
4378 #endif
4379
4380     /* set some entries to "unchangeable" according to other variables */
4381     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
4382         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
4383         (value_ptr == &setup.sound_music  && !audio.music_available) ||
4384         (value_ptr == &setup.fullscreen   && !video.fullscreen_available) ||
4385         (value_ptr == &screen_mode_text   && !video.fullscreen_available))
4386       setup_info[i].type |= TYPE_GHOSTED;
4387
4388     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
4389       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
4390     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
4391       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
4392     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
4393       initCursor(i, IMG_MENU_BUTTON);
4394
4395 #if 1
4396     DrawCursorAndText_Setup(i, FALSE);
4397 #else
4398     font_nr = getSetupTextFont(setup_info[i].type);
4399
4400     DrawText(mSX + xpos * 32, mSY + ypos * 32, setup_info[i].text, font_nr);
4401 #endif
4402
4403     if (setup_info[i].type & TYPE_VALUE)
4404       drawSetupValue(i);
4405
4406     num_setup_info++;
4407   }
4408
4409 #if 0
4410   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
4411                     "Joysticks deactivated in setup menu");
4412 #endif
4413
4414   FadeToFront();
4415   InitAnimation();
4416   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
4417 }
4418
4419 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
4420 {
4421   static int choice_store[MAX_SETUP_MODES];
4422   int choice = choice_store[setup_mode];        /* always starts with 0 */
4423   int x = 0;
4424   int y = choice;
4425
4426   if (button == MB_MENU_INITIALIZE)
4427   {
4428     /* advance to first valid menu entry */
4429     while (choice < num_setup_info &&
4430            setup_info[choice].type & TYPE_SKIP_ENTRY)
4431       choice++;
4432     choice_store[setup_mode] = choice;
4433
4434 #if 1
4435     DrawCursorAndText_Setup(choice, TRUE);
4436 #else
4437     drawCursor(choice, TRUE);
4438 #endif
4439
4440     return;
4441   }
4442   else if (button == MB_MENU_LEAVE)
4443   {
4444     PlaySound(SND_MENU_ITEM_SELECTING);
4445
4446     for (y = 0; y < num_setup_info; y++)
4447     {
4448       if (setup_info[y].type & TYPE_LEAVE_MENU)
4449       {
4450         void (*menu_callback_function)(void) = setup_info[y].value;
4451
4452         menu_callback_function();
4453
4454         break;  /* absolutely needed because function changes 'setup_info'! */
4455       }
4456     }
4457
4458     return;
4459   }
4460
4461   if (mx || my)         /* mouse input */
4462   {
4463     x = (mx - mSX) / 32;
4464     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
4465   }
4466   else if (dx || dy)    /* keyboard input */
4467   {
4468     if (dx)
4469     {
4470       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
4471
4472       if (setup_info[choice].type & menu_navigation_type ||
4473           setup_info[choice].type & TYPE_BOOLEAN_STYLE)
4474         button = MB_MENU_CHOICE;
4475     }
4476     else if (dy)
4477       y = choice + dy;
4478
4479     /* jump to next non-empty menu entry (up or down) */
4480     while (y > 0 && y < num_setup_info - 1 &&
4481            setup_info[y].type & TYPE_SKIP_ENTRY)
4482       y += dy;
4483   }
4484
4485   if (IN_VIS_FIELD(x, y) && y >= 0 && y < num_setup_info)
4486   {
4487     if (button)
4488     {
4489       if (y != choice && setup_info[y].type & ~TYPE_SKIP_ENTRY)
4490       {
4491         PlaySound(SND_MENU_ITEM_ACTIVATING);
4492
4493 #if 1
4494         DrawCursorAndText_Setup(choice, FALSE);
4495         DrawCursorAndText_Setup(y, TRUE);
4496 #else
4497         drawCursor(choice, FALSE);
4498         drawCursor(y, TRUE);
4499 #endif
4500
4501         choice = choice_store[setup_mode] = y;
4502       }
4503     }
4504     else if (!(setup_info[y].type & TYPE_GHOSTED))
4505     {
4506       PlaySound(SND_MENU_ITEM_SELECTING);
4507
4508       /* when selecting key headline, execute function for key value change */
4509       if (setup_info[y].type & TYPE_KEYTEXT &&
4510           setup_info[y + 1].type & TYPE_KEY)
4511         y++;
4512
4513       /* when selecting string value, execute function for list selection */
4514       if (setup_info[y].type & TYPE_STRING && y > 0 &&
4515           setup_info[y - 1].type & TYPE_ENTER_LIST)
4516         y--;
4517
4518       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE)
4519       {
4520         void (*menu_callback_function)(void) = setup_info[y].value;
4521
4522         menu_callback_function();
4523       }
4524       else
4525       {
4526         if (setup_info[y].type & TYPE_VALUE)
4527           changeSetupValue(y);
4528       }
4529     }
4530   }
4531 }
4532
4533 void DrawSetupScreen_Input()
4534 {
4535 #if 1
4536   int i;
4537 #endif
4538
4539   ClearWindow();
4540
4541 #if 1
4542   setup_info = setup_info_input;
4543 #endif
4544
4545   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Setup Input");
4546
4547 #if 1
4548   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
4549   {
4550     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
4551       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
4552     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
4553       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
4554     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
4555       initCursor(i, IMG_MENU_BUTTON);
4556
4557     DrawCursorAndText_Setup(i, FALSE);
4558   }
4559 #else
4560   initCursor(0,  IMG_MENU_BUTTON);
4561   initCursor(1,  IMG_MENU_BUTTON);
4562   initCursor(2,  IMG_MENU_BUTTON_ENTER_MENU);
4563   initCursor(13, IMG_MENU_BUTTON_LEAVE_MENU);
4564
4565   DrawText(mSX + 32, mSY +  2 * 32, "Player:", FONT_MENU_1);
4566   DrawText(mSX + 32, mSY +  3 * 32, "Device:", FONT_MENU_1);
4567   DrawText(mSX + 32, mSY + 15 * 32, "Back",   FONT_MENU_1);
4568 #endif
4569
4570 #if 0
4571   DeactivateJoystickForCalibration();
4572 #endif
4573 #if 1
4574   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
4575                     "Joysticks deactivated on this screen");
4576 #endif
4577
4578   /* create gadgets for setup input menu screen */
4579   FreeScreenGadgets();
4580   CreateScreenGadgets();
4581
4582   /* map gadgets for setup input menu screen */
4583   MapScreenMenuGadgets(SCREEN_MASK_INPUT);
4584
4585   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
4586   FadeToFront();
4587   InitAnimation();
4588 }
4589
4590 static void setJoystickDeviceToNr(char *device_name, int device_nr)
4591 {
4592   if (device_name == NULL)
4593     return;
4594
4595   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
4596     device_nr = 0;
4597
4598   if (strlen(device_name) > 1)
4599   {
4600     char c1 = device_name[strlen(device_name) - 1];
4601     char c2 = device_name[strlen(device_name) - 2];
4602
4603     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
4604       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
4605   }
4606   else
4607     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
4608             strlen(device_name));
4609 }
4610
4611 static void drawPlayerSetupInputInfo(int player_nr, boolean active)
4612 {
4613   int i;
4614   static struct SetupKeyboardInfo custom_key;
4615   static struct
4616   {
4617     Key *key;
4618     char *text;
4619   } custom[] =
4620   {
4621     { &custom_key.left,  "Joystick Left"  },
4622     { &custom_key.right, "Joystick Right" },
4623     { &custom_key.up,    "Joystick Up"    },
4624     { &custom_key.down,  "Joystick Down"  },
4625     { &custom_key.snap,  "Button 1"       },
4626     { &custom_key.drop,  "Button 2"       }
4627   };
4628   static char *joystick_name[MAX_PLAYERS] =
4629   {
4630     "Joystick1",
4631     "Joystick2",
4632     "Joystick3",
4633     "Joystick4"
4634   };
4635   int text_font_nr = (active ? FONT_MENU_1_ACTIVE : FONT_MENU_1);
4636
4637   InitJoysticks();
4638
4639   custom_key = setup.input[player_nr].key;
4640
4641   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
4642            FONT_INPUT_1_ACTIVE);
4643
4644   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
4645                              TILEX, TILEY);
4646   DrawGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
4647                          PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
4648
4649   if (setup.input[player_nr].use_joystick)
4650   {
4651     char *device_name = setup.input[player_nr].joy.device_name;
4652     char *text = joystick_name[getJoystickNrFromDeviceName(device_name)];
4653     int font_nr = (joystick.fd[player_nr] < 0 ? FONT_VALUE_OLD : FONT_VALUE_1);
4654
4655     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
4656     DrawText(mSX + 32, mSY + 4 * 32, "Calibrate", text_font_nr);
4657   }
4658   else
4659   {
4660     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", FONT_VALUE_1);
4661     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", text_font_nr);
4662   }
4663
4664   DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", FONT_MENU_1);
4665
4666   drawCursorXY(1, 4, IMG_MENU_BUTTON_LEFT);
4667   drawCursorXY(1, 5, IMG_MENU_BUTTON_RIGHT);
4668   drawCursorXY(1, 6, IMG_MENU_BUTTON_UP);
4669   drawCursorXY(1, 7, IMG_MENU_BUTTON_DOWN);
4670
4671   DrawText(mSX + 2 * 32, mSY +  6 * 32, ":", FONT_VALUE_OLD);
4672   DrawText(mSX + 2 * 32, mSY +  7 * 32, ":", FONT_VALUE_OLD);
4673   DrawText(mSX + 2 * 32, mSY +  8 * 32, ":", FONT_VALUE_OLD);
4674   DrawText(mSX + 2 * 32, mSY +  9 * 32, ":", FONT_VALUE_OLD);
4675   DrawText(mSX + 1 * 32, mSY + 10 * 32, "Snap Field:", FONT_VALUE_OLD);
4676   DrawText(mSX + 1 * 32, mSY + 12 * 32, "Drop Element:", FONT_VALUE_OLD);
4677
4678   for (i = 0; i < 6; i++)
4679   {
4680     int ypos = 6 + i + (i > 3 ? i-3 : 0);
4681
4682     DrawText(mSX + 3 * 32, mSY + ypos * 32,
4683              "              ", FONT_VALUE_1);
4684     DrawText(mSX + 3 * 32, mSY + ypos * 32,
4685              (setup.input[player_nr].use_joystick ?
4686               custom[i].text :
4687               getKeyNameFromKey(*custom[i].key)), FONT_VALUE_1);
4688   }
4689 }
4690
4691 static int input_player_nr = 0;
4692
4693 void HandleSetupScreen_Input_Player(int step, int direction)
4694 {
4695   int old_player_nr = input_player_nr;
4696   int new_player_nr;
4697
4698   new_player_nr = old_player_nr + step * direction;
4699   if (new_player_nr < 0)
4700     new_player_nr = 0;
4701   if (new_player_nr > MAX_PLAYERS - 1)
4702     new_player_nr = MAX_PLAYERS - 1;
4703
4704   if (new_player_nr != old_player_nr)
4705   {
4706     input_player_nr = new_player_nr;
4707
4708     drawPlayerSetupInputInfo(input_player_nr, FALSE);
4709   }
4710 }
4711
4712 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
4713 {
4714   static int choice = 0;
4715   int x = 0;
4716   int y = choice;
4717   int pos_start  = SETUPINPUT_SCREEN_POS_START;
4718   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
4719   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
4720   int pos_end    = SETUPINPUT_SCREEN_POS_END;
4721
4722   if (button == MB_MENU_INITIALIZE)
4723   {
4724     drawPlayerSetupInputInfo(input_player_nr, (choice == 2));
4725
4726 #if 1
4727     DrawCursorAndText_Setup(choice, TRUE);
4728 #else
4729     drawCursor(choice, TRUE);
4730 #endif
4731
4732     return;
4733   }
4734   else if (button == MB_MENU_LEAVE)
4735   {
4736     setup_mode = SETUP_MODE_MAIN;
4737     DrawSetupScreen();
4738     InitJoysticks();
4739
4740     return;
4741   }
4742
4743   if (mx || my)         /* mouse input */
4744   {
4745     x = (mx - mSX) / 32;
4746     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
4747   }
4748   else if (dx || dy)    /* keyboard input */
4749   {
4750     if (dx && choice == 0)
4751       x = (dx < 0 ? 10 : 12);
4752     else if ((dx && choice == 1) ||
4753              (dx == +1 && choice == 2) ||
4754              (dx == -1 && choice == pos_end))
4755       button = MB_MENU_CHOICE;
4756     else if (dy)
4757       y = choice + dy;
4758
4759     if (y >= pos_empty1 && y <= pos_empty2)
4760       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
4761   }
4762
4763   if (y == 0 && dx != 0 && button)
4764   {
4765     HandleSetupScreen_Input_Player(1, dx < 0 ? -1 : +1);
4766   }
4767   else if (IN_VIS_FIELD(x, y) &&
4768            y >= pos_start && y <= pos_end &&
4769            !(y >= pos_empty1 && y <= pos_empty2))
4770   {
4771     if (button)
4772     {
4773       if (y != choice)
4774       {
4775 #if 1
4776         DrawCursorAndText_Setup(choice, FALSE);
4777         DrawCursorAndText_Setup(y, TRUE);
4778
4779         drawPlayerSetupInputInfo(input_player_nr, (y == 2));
4780 #else
4781         drawCursor(choice, FALSE);
4782         drawCursor(y, TRUE);
4783 #endif
4784
4785         choice = y;
4786       }
4787     }
4788     else
4789     {
4790       if (y == 1)
4791       {
4792         char *device_name = setup.input[input_player_nr].joy.device_name;
4793
4794         if (!setup.input[input_player_nr].use_joystick)
4795         {
4796           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
4797
4798           setJoystickDeviceToNr(device_name, new_device_nr);
4799           setup.input[input_player_nr].use_joystick = TRUE;
4800         }
4801         else
4802         {
4803           int device_nr = getJoystickNrFromDeviceName(device_name);
4804           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
4805
4806           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
4807             setup.input[input_player_nr].use_joystick = FALSE;
4808           else
4809             setJoystickDeviceToNr(device_name, new_device_nr);
4810         }
4811
4812         drawPlayerSetupInputInfo(input_player_nr, FALSE);
4813       }
4814       else if (y == 2)
4815       {
4816         if (setup.input[input_player_nr].use_joystick)
4817         {
4818           InitJoysticks();
4819           CalibrateJoystick(input_player_nr);
4820         }
4821         else
4822           CustomizeKeyboard(input_player_nr);
4823       }
4824       else if (y == pos_end)
4825       {
4826         InitJoysticks();
4827
4828         setup_mode = SETUP_MODE_MAIN;
4829         DrawSetupScreen();
4830       }
4831     }
4832   }
4833 }
4834
4835 void CustomizeKeyboard(int player_nr)
4836 {
4837   int i;
4838   int step_nr;
4839   boolean finished = FALSE;
4840   static struct SetupKeyboardInfo custom_key;
4841   static struct
4842   {
4843     Key *key;
4844     char *text;
4845   } customize_step[] =
4846   {
4847     { &custom_key.left,  "Move Left"    },
4848     { &custom_key.right, "Move Right"   },
4849     { &custom_key.up,    "Move Up"      },
4850     { &custom_key.down,  "Move Down"    },
4851     { &custom_key.snap,  "Snap Field"   },
4852     { &custom_key.drop,  "Drop Element" }
4853   };
4854
4855   /* read existing key bindings from player setup */
4856   custom_key = setup.input[player_nr].key;
4857
4858   ClearWindow();
4859
4860   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
4861
4862   BackToFront();
4863   InitAnimation();
4864
4865   step_nr = 0;
4866   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
4867            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
4868   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
4869            "Key:", FONT_INPUT_1_ACTIVE);
4870   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4871            getKeyNameFromKey(*customize_step[step_nr].key), FONT_VALUE_OLD);
4872
4873   while (!finished)
4874   {
4875     if (PendingEvent())         /* got event */
4876     {
4877       Event event;
4878
4879       NextEvent(&event);
4880
4881       switch (event.type)
4882       {
4883         case EVENT_KEYPRESS:
4884           {
4885             Key key = GetEventKey((KeyEvent *)&event, FALSE);
4886
4887             if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
4888             {
4889               finished = TRUE;
4890               break;
4891             }
4892
4893             /* all keys configured -- wait for "Escape" or "Return" key */
4894             if (step_nr == 6)
4895               break;
4896
4897             /* press 'Enter' to keep the existing key binding */
4898             if (key == KSYM_Return)
4899               key = *customize_step[step_nr].key;
4900
4901             /* check if key already used */
4902             for (i = 0; i < step_nr; i++)
4903               if (*customize_step[i].key == key)
4904                 break;
4905             if (i < step_nr)
4906               break;
4907
4908             /* got new key binding */
4909             *customize_step[step_nr].key = key;
4910             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4911                      "             ", FONT_VALUE_1);
4912             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4913                      getKeyNameFromKey(key), FONT_VALUE_1);
4914             step_nr++;
4915
4916             /* un-highlight last query */
4917             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
4918                      customize_step[step_nr - 1].text, FONT_MENU_1);
4919             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
4920                      "Key:", FONT_MENU_1);
4921
4922             /* press 'Enter' to leave */
4923             if (step_nr == 6)
4924             {
4925               DrawText(mSX + 16, mSY + 15 * 32 + 16,
4926                        "Press Enter", FONT_TITLE_1);
4927               break;
4928             }
4929
4930             /* query next key binding */
4931             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
4932                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
4933             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
4934                      "Key:", FONT_INPUT_1_ACTIVE);
4935             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4936                      getKeyNameFromKey(*customize_step[step_nr].key),
4937                      FONT_VALUE_OLD);
4938           }
4939           break;
4940
4941         case EVENT_KEYRELEASE:
4942           key_joystick_mapping = 0;
4943           break;
4944
4945         default:
4946           HandleOtherEvents(&event);
4947           break;
4948       }
4949     }
4950
4951     DoAnimation();
4952     BackToFront();
4953
4954     /* don't eat all CPU time */
4955     Delay(10);
4956   }
4957
4958   /* write new key bindings back to player setup */
4959   setup.input[player_nr].key = custom_key;
4960
4961   StopAnimation();
4962   DrawSetupScreen_Input();
4963 }
4964
4965 static boolean CalibrateJoystickMain(int player_nr)
4966 {
4967   int new_joystick_xleft = JOYSTICK_XMIDDLE;
4968   int new_joystick_xright = JOYSTICK_XMIDDLE;
4969   int new_joystick_yupper = JOYSTICK_YMIDDLE;
4970   int new_joystick_ylower = JOYSTICK_YMIDDLE;
4971   int new_joystick_xmiddle, new_joystick_ymiddle;
4972
4973   int joystick_fd = joystick.fd[player_nr];
4974   int x, y, last_x, last_y, xpos = 8, ypos = 3;
4975   boolean check[3][3];
4976   int check_remaining = 3 * 3;
4977   int joy_x, joy_y;
4978   int joy_value;
4979   int result = -1;
4980
4981   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
4982     return FALSE;
4983
4984   if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
4985     return FALSE;
4986
4987   ClearWindow();
4988
4989   for (y = 0; y < 3; y++)
4990   {
4991     for (x = 0; x < 3; x++)
4992     {
4993       DrawGraphic(xpos + x - 1, ypos + y - 1, IMG_MENU_CALIBRATE_BLUE, 0);
4994       check[x][y] = FALSE;
4995     }
4996   }
4997
4998   DrawTextSCentered(mSY - SY +  6 * 32, FONT_TITLE_1, "Rotate joystick");
4999   DrawTextSCentered(mSY - SY +  7 * 32, FONT_TITLE_1, "in all directions");
5000   DrawTextSCentered(mSY - SY +  9 * 32, FONT_TITLE_1, "if all balls");
5001   DrawTextSCentered(mSY - SY + 10 * 32, FONT_TITLE_1, "are marked,");
5002   DrawTextSCentered(mSY - SY + 11 * 32, FONT_TITLE_1, "center joystick");
5003   DrawTextSCentered(mSY - SY + 12 * 32, FONT_TITLE_1, "and");
5004   DrawTextSCentered(mSY - SY + 13 * 32, FONT_TITLE_1, "press any button!");
5005
5006   joy_value = Joystick(player_nr);
5007   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
5008   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
5009
5010   /* eventually uncalibrated center position (joystick could be uncentered) */
5011   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
5012     return FALSE;
5013
5014   new_joystick_xmiddle = joy_x;
5015   new_joystick_ymiddle = joy_y;
5016
5017   DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_RED, 0);
5018   BackToFront();
5019
5020   while (Joystick(player_nr) & JOY_BUTTON);     /* wait for released button */
5021   InitAnimation();
5022
5023   while (result < 0)
5024   {
5025     if (PendingEvent())         /* got event */
5026     {
5027       Event event;
5028
5029       NextEvent(&event);
5030
5031       switch (event.type)
5032       {
5033         case EVENT_KEYPRESS:
5034           switch (GetEventKey((KeyEvent *)&event, TRUE))
5035           {
5036             case KSYM_Return:
5037               if (check_remaining == 0)
5038                 result = 1;
5039               break;
5040
5041             case KSYM_Escape:
5042               result = 0;
5043               break;
5044
5045             default:
5046               break;
5047           }
5048           break;
5049
5050         case EVENT_KEYRELEASE:
5051           key_joystick_mapping = 0;
5052           break;
5053
5054         default:
5055           HandleOtherEvents(&event);
5056           break;
5057       }
5058     }
5059
5060     if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
5061       return FALSE;
5062
5063     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
5064     new_joystick_xright = MAX(new_joystick_xright, joy_x);
5065     new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
5066     new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
5067
5068     setup.input[player_nr].joy.xleft = new_joystick_xleft;
5069     setup.input[player_nr].joy.yupper = new_joystick_yupper;
5070     setup.input[player_nr].joy.xright = new_joystick_xright;
5071     setup.input[player_nr].joy.ylower = new_joystick_ylower;
5072     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
5073     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
5074
5075     CheckJoystickData();
5076
5077     joy_value = Joystick(player_nr);
5078
5079     if (joy_value & JOY_BUTTON && check_remaining == 0)
5080       result = 1;
5081
5082     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
5083     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
5084
5085     if (x != last_x || y != last_y)
5086     {
5087       DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_YELLOW, 0);
5088       DrawGraphic(xpos + x,      ypos + y,      IMG_MENU_CALIBRATE_RED,    0);
5089
5090       last_x = x;
5091       last_y = y;
5092
5093       if (check_remaining > 0 && !check[x+1][y+1])
5094       {
5095         check[x+1][y+1] = TRUE;
5096         check_remaining--;
5097       }
5098
5099 #if 0
5100 #ifdef DEBUG
5101       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
5102              setup.input[player_nr].joy.xleft,
5103              setup.input[player_nr].joy.xmiddle,
5104              setup.input[player_nr].joy.xright);
5105       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
5106              setup.input[player_nr].joy.yupper,
5107              setup.input[player_nr].joy.ymiddle,
5108              setup.input[player_nr].joy.ylower);
5109 #endif
5110 #endif
5111
5112     }
5113
5114     DoAnimation();
5115     BackToFront();
5116
5117     /* don't eat all CPU time */
5118     Delay(10);
5119   }
5120
5121   /* calibrated center position (joystick should now be centered) */
5122   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
5123     return FALSE;
5124
5125   new_joystick_xmiddle = joy_x;
5126   new_joystick_ymiddle = joy_y;
5127
5128   StopAnimation();
5129
5130 #if 0
5131   DrawSetupScreen_Input();
5132 #endif
5133
5134   /* wait until the last pressed button was released */
5135   while (Joystick(player_nr) & JOY_BUTTON)
5136   {
5137     if (PendingEvent())         /* got event */
5138     {
5139       Event event;
5140
5141       NextEvent(&event);
5142       HandleOtherEvents(&event);
5143
5144       Delay(10);
5145     }
5146   }
5147
5148   return TRUE;
5149 }
5150
5151 void CalibrateJoystick(int player_nr)
5152 {
5153   if (!CalibrateJoystickMain(player_nr))
5154   {
5155     char *device_name = setup.input[player_nr].joy.device_name;
5156     int nr = getJoystickNrFromDeviceName(device_name) + 1;
5157     int xpos = mSX - SX;
5158     int ypos = mSY - SY;
5159
5160     ClearWindow();
5161
5162     DrawTextF(xpos + 16, ypos + 6 * 32, FONT_TITLE_1, "   JOYSTICK %d   ", nr);
5163     DrawTextF(xpos + 16, ypos + 7 * 32, FONT_TITLE_1, " NOT AVAILABLE! ");
5164     BackToFront();
5165
5166     Delay(2000);                /* show error message for a short time */
5167
5168     ClearEventQueue();
5169   }
5170
5171 #if 1
5172   DrawSetupScreen_Input();
5173 #endif
5174 }
5175
5176 void DrawSetupScreen()
5177 {
5178   DeactivateJoystick();
5179
5180   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
5181
5182   if (setup_mode == SETUP_MODE_INPUT)
5183     DrawSetupScreen_Input();
5184   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
5185     DrawChooseTree(&game_speed_current);
5186   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
5187     DrawChooseTree(&screen_mode_current);
5188   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
5189     DrawChooseTree(&artwork.gfx_current);
5190   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
5191     DrawChooseTree(&artwork.snd_current);
5192   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
5193     DrawChooseTree(&artwork.mus_current);
5194   else
5195     DrawSetupScreen_Generic();
5196
5197   PlayMenuSound();
5198   PlayMenuMusic();
5199 }
5200
5201 void RedrawSetupScreenAfterFullscreenToggle()
5202 {
5203   if (setup_mode == SETUP_MODE_GRAPHICS)
5204     DrawSetupScreen();
5205 }
5206
5207 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
5208 {
5209   if (setup_mode == SETUP_MODE_INPUT)
5210     HandleSetupScreen_Input(mx, my, dx, dy, button);
5211   else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)
5212     HandleChooseTree(mx, my, dx, dy, button, &game_speed_current);
5213   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
5214     HandleChooseTree(mx, my, dx, dy, button, &screen_mode_current);
5215   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
5216     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
5217   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
5218     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
5219   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
5220     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
5221   else
5222     HandleSetupScreen_Generic(mx, my, dx, dy, button);
5223
5224   DoAnimation();
5225 }
5226
5227 void HandleGameActions()
5228 {
5229   if (game_status != GAME_MODE_PLAYING)
5230     return;
5231
5232   GameActions();        /* main game loop */
5233
5234   if (tape.auto_play && !tape.playing)
5235     AutoPlayTape();     /* continue automatically playing next tape */
5236 }
5237
5238
5239 /* ---------- new screen button stuff -------------------------------------- */
5240
5241 static void getScreenMenuButtonPos(int *x, int *y, int gadget_id)
5242 {
5243   switch (gadget_id)
5244   {
5245 #if 1
5246     case SCREEN_CTRL_ID_PREV_LEVEL:
5247       *x = mSX + menu.main.button.prev_level.x;
5248       *y = mSY + menu.main.button.prev_level.y;
5249       break;
5250
5251     case SCREEN_CTRL_ID_NEXT_LEVEL:
5252       *x = mSX + menu.main.button.next_level.x;
5253       *y = mSY + menu.main.button.next_level.y;
5254       break;
5255 #else
5256     case SCREEN_CTRL_ID_PREV_LEVEL:
5257       *x = mSX + TILEX * getPrevlevelButtonPos();
5258       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
5259       break;
5260
5261     case SCREEN_CTRL_ID_NEXT_LEVEL:
5262       *x = mSX + TILEX * getNextLevelButtonPos();
5263       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
5264       break;
5265 #endif
5266
5267     case SCREEN_CTRL_ID_PREV_PLAYER:
5268       *x = mSX + TILEX * 10;
5269       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
5270       break;
5271
5272     case SCREEN_CTRL_ID_NEXT_PLAYER:
5273       *x = mSX + TILEX * 12;
5274       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
5275       break;
5276
5277     default:
5278       Error(ERR_EXIT, "unknown gadget ID %d", gadget_id);
5279   }
5280 }
5281
5282 static struct
5283 {
5284   int gfx_unpressed, gfx_pressed;
5285   void (*get_gadget_position)(int *, int *, int);
5286   int gadget_id;
5287   int screen_mask;
5288   char *infotext;
5289 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
5290 {
5291   {
5292     IMG_MENU_BUTTON_PREV_LEVEL, IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE,
5293     getScreenMenuButtonPos,
5294     SCREEN_CTRL_ID_PREV_LEVEL,
5295     SCREEN_MASK_MAIN,
5296     "last level"
5297   },
5298   {
5299     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
5300     getScreenMenuButtonPos,
5301     SCREEN_CTRL_ID_NEXT_LEVEL,
5302     SCREEN_MASK_MAIN,
5303     "next level"
5304   },
5305   {
5306     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
5307     getScreenMenuButtonPos,
5308     SCREEN_CTRL_ID_PREV_PLAYER,
5309     SCREEN_MASK_INPUT,
5310     "last player"
5311   },
5312   {
5313     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
5314     getScreenMenuButtonPos,
5315     SCREEN_CTRL_ID_NEXT_PLAYER,
5316     SCREEN_MASK_INPUT,
5317     "next player"
5318   },
5319 };
5320
5321 static struct
5322 {
5323   int gfx_unpressed, gfx_pressed;
5324   int x, y;
5325   int gadget_id;
5326   char *infotext;
5327 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
5328 {
5329   {
5330     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
5331     SC_SCROLL_UP_XPOS, SC_SCROLL_UP_YPOS,
5332     SCREEN_CTRL_ID_SCROLL_UP,
5333     "scroll up"
5334   },
5335   {
5336     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
5337     SC_SCROLL_DOWN_XPOS, SC_SCROLL_DOWN_YPOS,
5338     SCREEN_CTRL_ID_SCROLL_DOWN,
5339     "scroll down"
5340   }
5341 };
5342
5343 static struct
5344 {
5345 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
5346   Bitmap **gfx_unpressed, **gfx_pressed;
5347 #else
5348   int gfx_unpressed, gfx_pressed;
5349 #endif
5350   int x, y;
5351   int width, height;
5352   int type;
5353   int gadget_id;
5354   char *infotext;
5355 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
5356 {
5357   {
5358 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
5359     &scrollbar_bitmap[0], &scrollbar_bitmap[1],
5360 #else
5361     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
5362 #endif
5363     SC_SCROLL_VERTICAL_XPOS, SC_SCROLL_VERTICAL_YPOS,
5364     SC_SCROLL_VERTICAL_XSIZE, SC_SCROLL_VERTICAL_YSIZE,
5365     GD_TYPE_SCROLLBAR_VERTICAL,
5366     SCREEN_CTRL_ID_SCROLL_VERTICAL,
5367     "scroll level series vertically"
5368   }
5369 };
5370
5371 static void CreateScreenMenubuttons()
5372 {
5373   struct GadgetInfo *gi;
5374   unsigned long event_mask;
5375   int i;
5376
5377   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
5378   {
5379     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
5380     int gfx_unpressed, gfx_pressed;
5381     int x, y, width, height;
5382     int gd_x1, gd_x2, gd_y1, gd_y2;
5383     int id = menubutton_info[i].gadget_id;
5384
5385     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
5386
5387     menubutton_info[i].get_gadget_position(&x, &y, id);
5388
5389     width = SC_MENUBUTTON_XSIZE;
5390     height = SC_MENUBUTTON_YSIZE;
5391
5392     gfx_unpressed = menubutton_info[i].gfx_unpressed;
5393     gfx_pressed   = menubutton_info[i].gfx_pressed;
5394     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
5395     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
5396     gd_x1 = graphic_info[gfx_unpressed].src_x;
5397     gd_y1 = graphic_info[gfx_unpressed].src_y;
5398     gd_x2 = graphic_info[gfx_pressed].src_x;
5399     gd_y2 = graphic_info[gfx_pressed].src_y;
5400
5401     gi = CreateGadget(GDI_CUSTOM_ID, id,
5402                       GDI_CUSTOM_TYPE_ID, i,
5403                       GDI_INFO_TEXT, menubutton_info[i].infotext,
5404                       GDI_X, x,
5405                       GDI_Y, y,
5406                       GDI_WIDTH, width,
5407                       GDI_HEIGHT, height,
5408                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5409                       GDI_STATE, GD_BUTTON_UNPRESSED,
5410                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
5411                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
5412                       GDI_DIRECT_DRAW, FALSE,
5413                       GDI_EVENT_MASK, event_mask,
5414                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
5415                       GDI_END);
5416
5417     if (gi == NULL)
5418       Error(ERR_EXIT, "cannot create gadget");
5419
5420     screen_gadget[id] = gi;
5421   }
5422 }
5423
5424 static void CreateScreenScrollbuttons()
5425 {
5426   struct GadgetInfo *gi;
5427   unsigned long event_mask;
5428   int i;
5429
5430   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
5431   {
5432     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
5433     int gfx_unpressed, gfx_pressed;
5434     int x, y, width, height;
5435     int gd_x1, gd_x2, gd_y1, gd_y2;
5436     int id = scrollbutton_info[i].gadget_id;
5437
5438     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
5439
5440     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
5441     y = mSY + scrollbutton_info[i].y;
5442     width = SC_SCROLLBUTTON_XSIZE;
5443     height = SC_SCROLLBUTTON_YSIZE;
5444
5445     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
5446       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
5447                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
5448
5449     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
5450     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
5451     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
5452     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
5453     gd_x1 = graphic_info[gfx_unpressed].src_x;
5454     gd_y1 = graphic_info[gfx_unpressed].src_y;
5455     gd_x2 = graphic_info[gfx_pressed].src_x;
5456     gd_y2 = graphic_info[gfx_pressed].src_y;
5457
5458     gi = CreateGadget(GDI_CUSTOM_ID, id,
5459                       GDI_CUSTOM_TYPE_ID, i,
5460                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
5461                       GDI_X, x,
5462                       GDI_Y, y,
5463                       GDI_WIDTH, width,
5464                       GDI_HEIGHT, height,
5465                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5466                       GDI_STATE, GD_BUTTON_UNPRESSED,
5467                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
5468                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
5469                       GDI_DIRECT_DRAW, FALSE,
5470                       GDI_EVENT_MASK, event_mask,
5471                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
5472                       GDI_END);
5473
5474     if (gi == NULL)
5475       Error(ERR_EXIT, "cannot create gadget");
5476
5477     screen_gadget[id] = gi;
5478   }
5479 }
5480
5481 static void CreateScreenScrollbars()
5482 {
5483   int i;
5484
5485   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
5486   {
5487     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
5488 #if !defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
5489     int gfx_unpressed, gfx_pressed;
5490 #endif
5491     int x, y, width, height;
5492     int gd_x1, gd_x2, gd_y1, gd_y2;
5493     struct GadgetInfo *gi;
5494     int items_max, items_visible, item_position;
5495     unsigned long event_mask;
5496     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
5497     int id = scrollbar_info[i].gadget_id;
5498
5499     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
5500
5501     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
5502     y = mSY + scrollbar_info[i].y;
5503     width  = scrollbar_info[i].width;
5504     height = scrollbar_info[i].height;
5505
5506     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
5507       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
5508
5509     items_max = num_page_entries;
5510     items_visible = num_page_entries;
5511     item_position = 0;
5512
5513 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
5514     gd_bitmap_unpressed = *scrollbar_info[i].gfx_unpressed;
5515     gd_bitmap_pressed   = *scrollbar_info[i].gfx_pressed;
5516     gd_x1 = 0;
5517     gd_y1 = 0;
5518     gd_x2 = 0;
5519     gd_y2 = 0;
5520 #else
5521     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
5522     gfx_pressed   = scrollbar_info[i].gfx_pressed;
5523     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
5524     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
5525     gd_x1 = graphic_info[gfx_unpressed].src_x;
5526     gd_y1 = graphic_info[gfx_unpressed].src_y;
5527     gd_x2 = graphic_info[gfx_pressed].src_x;
5528     gd_y2 = graphic_info[gfx_pressed].src_y;
5529 #endif
5530
5531     gi = CreateGadget(GDI_CUSTOM_ID, id,
5532                       GDI_CUSTOM_TYPE_ID, i,
5533                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
5534                       GDI_X, x,
5535                       GDI_Y, y,
5536                       GDI_WIDTH, width,
5537                       GDI_HEIGHT, height,
5538                       GDI_TYPE, scrollbar_info[i].type,
5539                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
5540                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
5541                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
5542 #if 1
5543                       GDI_WHEEL_AREA_X, SX,
5544                       GDI_WHEEL_AREA_Y, SY,
5545                       GDI_WHEEL_AREA_WIDTH, SXSIZE,
5546                       GDI_WHEEL_AREA_HEIGHT, SYSIZE,
5547 #else
5548                       GDI_WHEEL_AREA_X, 0,
5549                       GDI_WHEEL_AREA_Y, 0,
5550                       GDI_WHEEL_AREA_WIDTH, WIN_XSIZE,
5551                       GDI_WHEEL_AREA_HEIGHT, WIN_YSIZE,
5552 #endif
5553                       GDI_STATE, GD_BUTTON_UNPRESSED,
5554                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
5555                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
5556                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
5557                       GDI_DIRECT_DRAW, FALSE,
5558                       GDI_EVENT_MASK, event_mask,
5559                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
5560                       GDI_END);
5561
5562     if (gi == NULL)
5563       Error(ERR_EXIT, "cannot create gadget");
5564
5565     screen_gadget[id] = gi;
5566   }
5567 }
5568
5569 void CreateScreenGadgets()
5570 {
5571   int last_game_status = game_status;   /* save current game status */
5572
5573 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
5574   int i;
5575
5576   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
5577   {
5578     scrollbar_bitmap[i] = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
5579
5580     /* copy pointers to clip mask and GC */
5581     scrollbar_bitmap[i]->clip_mask =
5582       graphic_info[IMG_MENU_SCROLLBAR + i].clip_mask;
5583     scrollbar_bitmap[i]->stored_clip_gc =
5584       graphic_info[IMG_MENU_SCROLLBAR + i].clip_gc;
5585
5586     BlitBitmap(graphic_info[IMG_MENU_SCROLLBAR + i].bitmap,
5587                scrollbar_bitmap[i],
5588                graphic_info[IMG_MENU_SCROLLBAR + i].src_x,
5589                graphic_info[IMG_MENU_SCROLLBAR + i].src_y,
5590                TILEX, TILEY, 0, 0);
5591   }
5592 #endif
5593
5594   CreateScreenMenubuttons();
5595
5596   /* force LEVELS draw offset for scrollbar / scrollbutton gadgets */
5597   game_status = GAME_MODE_LEVELS;
5598
5599   CreateScreenScrollbuttons();
5600   CreateScreenScrollbars();
5601
5602   game_status = last_game_status;       /* restore current game status */
5603 }
5604
5605 void FreeScreenGadgets()
5606 {
5607   int i;
5608
5609 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
5610   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
5611   {
5612     /* prevent freeing clip mask and GC twice */
5613     scrollbar_bitmap[i]->clip_mask = None;
5614     scrollbar_bitmap[i]->stored_clip_gc = None;
5615
5616     FreeBitmap(scrollbar_bitmap[i]);
5617   }
5618 #endif
5619
5620   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
5621     FreeGadget(screen_gadget[i]);
5622 }
5623
5624 void MapScreenMenuGadgets(int screen_mask)
5625 {
5626   int i;
5627
5628   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
5629     if (screen_mask & menubutton_info[i].screen_mask)
5630       MapGadget(screen_gadget[menubutton_info[i].gadget_id]);
5631 }
5632
5633 void MapScreenTreeGadgets(TreeInfo *ti)
5634 {
5635   int num_entries = numTreeInfoInGroup(ti);
5636   int i;
5637
5638   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
5639     return;
5640
5641   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
5642     MapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
5643
5644   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
5645     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
5646 }
5647
5648 static void HandleScreenGadgets(struct GadgetInfo *gi)
5649 {
5650   int id = gi->custom_id;
5651   int button = gi->event.button;
5652   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
5653
5654   switch (id)
5655   {
5656     case SCREEN_CTRL_ID_PREV_LEVEL:
5657       HandleMainMenu_SelectLevel(step, -1);
5658       break;
5659
5660     case SCREEN_CTRL_ID_NEXT_LEVEL:
5661       HandleMainMenu_SelectLevel(step, +1);
5662       break;
5663
5664     case SCREEN_CTRL_ID_PREV_PLAYER:
5665       HandleSetupScreen_Input_Player(step, -1);
5666       break;
5667
5668     case SCREEN_CTRL_ID_NEXT_PLAYER:
5669       HandleSetupScreen_Input_Player(step, +1);
5670       break;
5671
5672     case SCREEN_CTRL_ID_SCROLL_UP:
5673       if (game_status == GAME_MODE_LEVELS)
5674         HandleChooseLevel(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
5675       else if (game_status == GAME_MODE_SETUP)
5676         HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
5677       break;
5678
5679     case SCREEN_CTRL_ID_SCROLL_DOWN:
5680       if (game_status == GAME_MODE_LEVELS)
5681         HandleChooseLevel(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
5682       else if (game_status == GAME_MODE_SETUP)
5683         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
5684       break;
5685
5686     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
5687       if (game_status == GAME_MODE_LEVELS)
5688         HandleChooseLevel(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
5689       else if (game_status == GAME_MODE_SETUP)
5690         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
5691       break;
5692
5693     default:
5694       break;
5695   }
5696 }