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