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