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