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