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