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