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