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