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