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