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