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