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