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