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