rnd-20061029-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 #if 1
2565   /* clear tree list area, but not title or scrollbar */
2566   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
2567                  SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset,
2568                  NUM_MENU_ENTRIES_ON_SCREEN * 32);
2569 #else
2570   /* clear tree list area, but not title or scrollbar */
2571   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
2572                  SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset,
2573                  MAX_MENU_ENTRIES_ON_SCREEN * 32);
2574 #endif
2575
2576   for (i = 0; i < num_page_entries; i++)
2577   {
2578     TreeInfo *node, *node_first;
2579     int entry_pos = first_entry + i;
2580     int xpos = MENU_SCREEN_START_XPOS;
2581     int ypos = MENU_SCREEN_START_YPOS + i;
2582     int startx = mSX + xpos * 32;
2583     int starty = mSY + ypos * 32;
2584     int font_nr = FONT_TEXT_1;
2585     int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
2586     int startx_text = startx + font_xoffset;
2587     int startx_scrollbar = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
2588     int text_size = startx_scrollbar - startx_text;
2589     int max_buffer_len = text_size / getFontWidth(font_nr);
2590     char buffer[max_buffer_len + 1];
2591
2592     node_first = getTreeInfoFirstGroupEntry(ti);
2593     node = getTreeInfoFromPos(node_first, entry_pos);
2594
2595     strncpy(buffer, node->name, max_buffer_len);
2596     buffer[max_buffer_len] = '\0';
2597
2598     DrawText(startx, starty, buffer, font_nr + node->color);
2599
2600     if (node->parent_link)
2601       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
2602     else if (node->level_group)
2603       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
2604     else
2605       initCursor(i, IMG_MENU_BUTTON);
2606   }
2607
2608   game_status = last_game_status;       /* restore current game status */
2609
2610   redraw_mask |= REDRAW_FIELD;
2611 }
2612
2613 static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
2614 {
2615   TreeInfo *node, *node_first;
2616   int x, last_redraw_mask = redraw_mask;
2617   int ypos = MENU_TITLE2_YPOS;
2618   int font_nr = FONT_TITLE_2;
2619
2620   if (ti->type != TREE_TYPE_LEVEL_DIR)
2621     return;
2622
2623   node_first = getTreeInfoFirstGroupEntry(ti);
2624   node = getTreeInfoFromPos(node_first, entry_pos);
2625
2626   DrawBackgroundForFont(SX, SY + ypos, SXSIZE, getFontHeight(font_nr), font_nr);
2627
2628   if (node->parent_link)
2629     DrawTextFCentered(ypos, font_nr, "leave group \"%s\"",
2630                       node->class_desc);
2631   else if (node->level_group)
2632     DrawTextFCentered(ypos, font_nr, "enter group \"%s\"",
2633                       node->class_desc);
2634   else if (ti->type == TREE_TYPE_LEVEL_DIR)
2635     DrawTextFCentered(ypos, font_nr, "%3d levels (%s)",
2636                       node->levels, node->class_desc);
2637
2638   /* let BackToFront() redraw only what is needed */
2639   redraw_mask = last_redraw_mask | REDRAW_TILES;
2640   for (x = 0; x < SCR_FIELDX; x++)
2641     MarkTileDirty(x, 1);
2642 }
2643
2644 static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
2645                              TreeInfo **ti_ptr)
2646 {
2647   TreeInfo *ti = *ti_ptr;
2648   int x = 0;
2649   int y = ti->cl_cursor;
2650   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2651   int num_entries = numTreeInfoInGroup(ti);
2652   int num_page_entries;
2653   int last_game_status = game_status;   /* save current game status */
2654   boolean position_set_by_scrollbar = (dx == 999);
2655
2656   /* force LEVELS draw offset on choose level and artwork setup screen */
2657   game_status = GAME_MODE_LEVELS;
2658
2659   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
2660     num_page_entries = num_entries;
2661   else
2662     num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
2663
2664   game_status = last_game_status;       /* restore current game status */
2665
2666   if (button == MB_MENU_INITIALIZE)
2667   {
2668     int num_entries = numTreeInfoInGroup(ti);
2669     int entry_pos = posTreeInfo(ti);
2670
2671     if (ti->cl_first == -1)
2672     {
2673       /* only on initialization */
2674       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
2675       ti->cl_cursor = entry_pos - ti->cl_first;
2676     }
2677     else if (ti->cl_cursor >= num_page_entries ||
2678              (num_entries > num_page_entries &&
2679               num_entries - ti->cl_first < num_page_entries))
2680     {
2681       /* only after change of list size (by custom graphic configuration) */
2682       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
2683       ti->cl_cursor = entry_pos - ti->cl_first;
2684     }
2685
2686     if (position_set_by_scrollbar)
2687       ti->cl_first = dy;
2688     else
2689       AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2690                                 ti->cl_first, ti);
2691
2692     drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2693     drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2694     drawChooseTreeCursor(ti->cl_cursor, TRUE);
2695
2696     return;
2697   }
2698   else if (button == MB_MENU_LEAVE)
2699   {
2700     if (ti->node_parent)
2701     {
2702       *ti_ptr = ti->node_parent;
2703       DrawChooseTree(ti_ptr);
2704     }
2705     else if (game_status == GAME_MODE_SETUP)
2706     {
2707       if (game_status == GAME_MODE_SETUP)
2708       {
2709         if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
2710           execSetupGraphics();
2711         else
2712           execSetupArtwork();
2713       }
2714     }
2715     else
2716     {
2717       game_status = GAME_MODE_MAIN;
2718       DrawMainMenu();
2719     }
2720
2721     return;
2722   }
2723
2724   if (mx || my)         /* mouse input */
2725   {
2726     int last_game_status = game_status; /* save current game status */
2727
2728     /* force LEVELS draw offset on artwork setup screen */
2729     game_status = GAME_MODE_LEVELS;
2730
2731     x = (mx - mSX) / 32;
2732     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
2733
2734     game_status = last_game_status;     /* restore current game status */
2735   }
2736   else if (dx || dy)    /* keyboard or scrollbar/scrollbutton input */
2737   {
2738     /* move cursor instead of scrolling when already at start/end of list */
2739     if (dy == -1 * SCROLL_LINE && ti->cl_first == 0)
2740       dy = -1;
2741     else if (dy == +1 * SCROLL_LINE &&
2742              ti->cl_first + num_page_entries == num_entries)
2743       dy = 1;
2744
2745     /* handle scrolling screen one line or page */
2746     if (ti->cl_cursor + dy < 0 ||
2747         ti->cl_cursor + dy > num_page_entries - 1)
2748     {
2749       if (ABS(dy) == SCROLL_PAGE)
2750         step = num_page_entries - 1;
2751
2752       if (dy < 0 && ti->cl_first > 0)
2753       {
2754         /* scroll page/line up */
2755
2756         ti->cl_first -= step;
2757         if (ti->cl_first < 0)
2758           ti->cl_first = 0;
2759
2760         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2761         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2762         drawChooseTreeCursor(ti->cl_cursor, TRUE);
2763
2764         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2765                                   ti->cl_first, ti);
2766       }
2767       else if (dy > 0 && ti->cl_first + num_page_entries < num_entries)
2768       {
2769         /* scroll page/line down */
2770
2771         ti->cl_first += step;
2772         if (ti->cl_first + num_page_entries > num_entries)
2773           ti->cl_first = MAX(0, num_entries - num_page_entries);
2774
2775         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2776         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2777         drawChooseTreeCursor(ti->cl_cursor, TRUE);
2778
2779         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2780                                   ti->cl_first, ti);
2781       }
2782
2783       return;
2784     }
2785
2786     /* handle moving cursor one line */
2787     y = ti->cl_cursor + dy;
2788   }
2789
2790   if (dx == 1)
2791   {
2792     TreeInfo *node_first, *node_cursor;
2793     int entry_pos = ti->cl_first + y;
2794
2795     node_first = getTreeInfoFirstGroupEntry(ti);
2796     node_cursor = getTreeInfoFromPos(node_first, entry_pos);
2797
2798     if (node_cursor->node_group)
2799     {
2800       node_cursor->cl_first = ti->cl_first;
2801       node_cursor->cl_cursor = ti->cl_cursor;
2802       *ti_ptr = node_cursor->node_group;
2803       DrawChooseTree(ti_ptr);
2804
2805       return;
2806     }
2807   }
2808   else if (dx == -1 && ti->node_parent)
2809   {
2810     *ti_ptr = ti->node_parent;
2811     DrawChooseTree(ti_ptr);
2812
2813     return;
2814   }
2815
2816   if (!anyScrollbarGadgetActive() &&
2817       IN_VIS_FIELD(x, y) &&
2818       mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
2819       y >= 0 && y < num_page_entries)
2820   {
2821     if (button)
2822     {
2823       if (y != ti->cl_cursor)
2824       {
2825         drawChooseTreeCursor(ti->cl_cursor, FALSE);
2826         drawChooseTreeCursor(y, TRUE);
2827         drawChooseTreeInfo(ti->cl_first + y, ti);
2828
2829         ti->cl_cursor = y;
2830       }
2831     }
2832     else
2833     {
2834       TreeInfo *node_first, *node_cursor;
2835       int entry_pos = ti->cl_first + y;
2836
2837       node_first = getTreeInfoFirstGroupEntry(ti);
2838       node_cursor = getTreeInfoFromPos(node_first, entry_pos);
2839
2840       if (node_cursor->node_group)
2841       {
2842         node_cursor->cl_first = ti->cl_first;
2843         node_cursor->cl_cursor = ti->cl_cursor;
2844         *ti_ptr = node_cursor->node_group;
2845         DrawChooseTree(ti_ptr);
2846       }
2847       else if (node_cursor->parent_link)
2848       {
2849         *ti_ptr = node_cursor->node_parent;
2850         DrawChooseTree(ti_ptr);
2851       }
2852       else
2853       {
2854         node_cursor->cl_first = ti->cl_first;
2855         node_cursor->cl_cursor = ti->cl_cursor;
2856         *ti_ptr = node_cursor;
2857
2858         if (ti->type == TREE_TYPE_LEVEL_DIR)
2859         {
2860           LoadLevelSetup_SeriesInfo();
2861
2862           SaveLevelSetup_LastSeries();
2863           SaveLevelSetup_SeriesInfo();
2864           TapeErase();
2865         }
2866
2867         if (game_status == GAME_MODE_SETUP)
2868         {
2869           if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
2870             execSetupGraphics();
2871           else
2872             execSetupArtwork();
2873         }
2874         else
2875         {
2876           game_status = GAME_MODE_MAIN;
2877           DrawMainMenu();
2878         }
2879       }
2880     }
2881   }
2882 }
2883
2884 void DrawChooseLevel()
2885 {
2886   SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
2887
2888   DrawChooseTree(&leveldir_current);
2889
2890   PlayMenuSound();
2891   PlayMenuMusic();
2892 }
2893
2894 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
2895 {
2896   HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
2897
2898   DoAnimation();
2899 }
2900
2901 void DrawHallOfFame(int highlight_position)
2902 {
2903   UnmapAllGadgets();
2904   FadeSoundsAndMusic();
2905
2906   /* (this is needed when called from GameEnd() after winning a game) */
2907   KeyboardAutoRepeatOn();
2908   ActivateJoystick();
2909
2910   /* (this is needed when called from GameEnd() after winning a game) */
2911   SetDrawDeactivationMask(REDRAW_NONE);
2912   SetDrawBackgroundMask(REDRAW_FIELD);
2913
2914   CloseDoor(DOOR_CLOSE_2);
2915
2916   if (highlight_position < 0) 
2917     LoadScore(level_nr);
2918
2919   FadeOut(REDRAW_FIELD);
2920
2921   InitAnimation();
2922
2923   PlayMenuSound();
2924   PlayMenuMusic();
2925
2926   HandleHallOfFame(highlight_position, 0, 0, 0, MB_MENU_INITIALIZE);
2927
2928   FadeIn(REDRAW_FIELD);
2929 }
2930
2931 static void drawHallOfFameList(int first_entry, int highlight_position)
2932 {
2933   int i;
2934
2935   SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
2936   ClearWindow();
2937
2938   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame");
2939   DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
2940                     "HighScores of Level %d", level_nr);
2941
2942   for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
2943   {
2944     int entry = first_entry + i;
2945     boolean active = (entry == highlight_position);
2946     int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
2947     int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
2948     int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
2949     int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
2950     int dx1 = 3 * getFontWidth(font_nr1);
2951     int dx2 = dx1 + getFontWidth(font_nr1);
2952     int dx3 = dx2 + 25 * getFontWidth(font_nr3);
2953     int sy = mSY + 64 + i * 32;
2954
2955     DrawText(mSX, sy, int2str(entry + 1, 3), font_nr1);
2956     DrawText(mSX + dx1, sy, ".", font_nr1);
2957     DrawText(mSX + dx2, sy, ".........................", font_nr3);
2958
2959     if (!strEqual(highscore[entry].Name, EMPTY_PLAYER_NAME))
2960       DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
2961
2962     DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
2963   }
2964
2965   redraw_mask |= REDRAW_FIELD;
2966 }
2967
2968 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
2969 {
2970   static int first_entry = 0;
2971   static int highlight_position = 0;
2972   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2973
2974   if (button == MB_MENU_INITIALIZE)
2975   {
2976     first_entry = 0;
2977     highlight_position = mx;
2978     drawHallOfFameList(first_entry, highlight_position);
2979
2980     return;
2981   }
2982
2983   if (ABS(dy) == SCROLL_PAGE)           /* handle scrolling one page */
2984     step = NUM_MENU_ENTRIES_ON_SCREEN - 1;
2985
2986   if (dy < 0)
2987   {
2988     if (first_entry > 0)
2989     {
2990       first_entry -= step;
2991       if (first_entry < 0)
2992         first_entry = 0;
2993
2994       drawHallOfFameList(first_entry, highlight_position);
2995     }
2996   }
2997   else if (dy > 0)
2998   {
2999     if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
3000     {
3001       first_entry += step;
3002       if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
3003         first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
3004
3005       drawHallOfFameList(first_entry, highlight_position);
3006     }
3007   }
3008   else if (button == MB_MENU_LEAVE)
3009   {
3010     FadeSound(SND_BACKGROUND_SCORES);
3011
3012     game_status = GAME_MODE_MAIN;
3013
3014     DrawMainMenu();
3015   }
3016   else if (button == MB_MENU_CHOICE)
3017   {
3018     FadeSound(SND_BACKGROUND_SCORES);
3019     FadeOut(REDRAW_FIELD);
3020
3021     game_status = GAME_MODE_MAIN;
3022
3023     DrawAndFadeInMainMenu(REDRAW_FIELD);
3024   }
3025
3026   if (game_status == GAME_MODE_SCORES)
3027     PlayMenuSoundIfLoop();
3028
3029   DoAnimation();
3030 }
3031
3032
3033 /* ========================================================================= */
3034 /* setup screen functions                                                    */
3035 /* ========================================================================= */
3036
3037 static struct TokenInfo *setup_info;
3038 static int num_setup_info;
3039
3040 static char *screen_mode_text;
3041 static char *graphics_set_name;
3042 static char *sounds_set_name;
3043 static char *music_set_name;
3044
3045 static void execSetupMain()
3046 {
3047   setup_mode = SETUP_MODE_MAIN;
3048   DrawSetupScreen();
3049 }
3050
3051 static void execSetupGame()
3052 {
3053   setup_mode = SETUP_MODE_GAME;
3054   DrawSetupScreen();
3055 }
3056
3057 static void execSetupEditor()
3058 {
3059   setup_mode = SETUP_MODE_EDITOR;
3060   DrawSetupScreen();
3061 }
3062
3063 static void execSetupGraphics()
3064 {
3065   if (video.fullscreen_available && screen_modes == NULL)
3066   {
3067     int i;
3068
3069     for (i = 0; video.fullscreen_modes[i].width != -1; i++)
3070     {
3071       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
3072       char identifier[32], name[32];
3073       int x = video.fullscreen_modes[i].width;
3074       int y = video.fullscreen_modes[i].height;
3075       int xx, yy;
3076
3077       get_aspect_ratio_from_screen_mode(&video.fullscreen_modes[i], &xx, &yy);
3078
3079       ti->node_top = &screen_modes;
3080       ti->sort_priority = x * 10000 + y;
3081
3082       sprintf(identifier, "%dx%d", x, y);
3083       sprintf(name,     "%d x %d [%d:%d]", x, y, xx, yy);
3084
3085       setString(&ti->identifier, identifier);
3086       setString(&ti->name, name);
3087       setString(&ti->name_sorting, name);
3088       setString(&ti->infotext, "Fullscreen Mode");
3089
3090       pushTreeInfo(&screen_modes, ti);
3091     }
3092
3093     /* sort fullscreen modes to start with lowest available screen resolution */
3094     sortTreeInfo(&screen_modes);
3095
3096     /* set current screen mode for fullscreen mode to configured setup value */
3097     screen_mode_current = getTreeInfoFromIdentifier(screen_modes,
3098                                                     setup.fullscreen_mode);
3099
3100     /* if that fails, set current screen mode to reliable default value */
3101     if (screen_mode_current == NULL)
3102       screen_mode_current = getTreeInfoFromIdentifier(screen_modes,
3103                                                       DEFAULT_FULLSCREEN_MODE);
3104
3105     /* if that also fails, set current screen mode to first available mode */
3106     if (screen_mode_current == NULL)
3107       screen_mode_current = screen_modes;
3108
3109     if (screen_mode_current == NULL)
3110       video.fullscreen_available = FALSE;
3111   }
3112
3113   if (video.fullscreen_available)
3114   {
3115     setup.fullscreen_mode = screen_mode_current->identifier;
3116
3117     /* needed for displaying screen mode name instead of identifier */
3118     screen_mode_text = screen_mode_current->name;
3119   }
3120
3121   setup_mode = SETUP_MODE_GRAPHICS;
3122   DrawSetupScreen();
3123 }
3124
3125 static void execSetupChooseScreenMode()
3126 {
3127   if (!video.fullscreen_available)
3128     return;
3129
3130   setup_mode = SETUP_MODE_CHOOSE_SCREEN_MODE;
3131   DrawSetupScreen();
3132 }
3133
3134 static void execSetupSound()
3135 {
3136   setup_mode = SETUP_MODE_SOUND;
3137   DrawSetupScreen();
3138 }
3139
3140 static void execSetupArtwork()
3141 {
3142   setup.graphics_set = artwork.gfx_current->identifier;
3143   setup.sounds_set = artwork.snd_current->identifier;
3144   setup.music_set = artwork.mus_current->identifier;
3145
3146   /* needed if last screen (setup choice) changed graphics, sounds or music */
3147   ReloadCustomArtwork(0);
3148
3149   /* needed for displaying artwork name instead of artwork identifier */
3150   graphics_set_name = artwork.gfx_current->name;
3151   sounds_set_name = artwork.snd_current->name;
3152   music_set_name = artwork.mus_current->name;
3153
3154   setup_mode = SETUP_MODE_ARTWORK;
3155   DrawSetupScreen();
3156 }
3157
3158 static void execSetupChooseGraphics()
3159 {
3160   setup_mode = SETUP_MODE_CHOOSE_GRAPHICS;
3161   DrawSetupScreen();
3162 }
3163
3164 static void execSetupChooseSounds()
3165 {
3166   setup_mode = SETUP_MODE_CHOOSE_SOUNDS;
3167   DrawSetupScreen();
3168 }
3169
3170 static void execSetupChooseMusic()
3171 {
3172   setup_mode = SETUP_MODE_CHOOSE_MUSIC;
3173   DrawSetupScreen();
3174 }
3175
3176 static void execSetupInput()
3177 {
3178   setup_mode = SETUP_MODE_INPUT;
3179   DrawSetupScreen();
3180 }
3181
3182 static void execSetupShortcut1()
3183 {
3184   setup_mode = SETUP_MODE_SHORTCUT_1;
3185   DrawSetupScreen();
3186 }
3187
3188 static void execSetupShortcut2()
3189 {
3190   setup_mode = SETUP_MODE_SHORTCUT_2;
3191   DrawSetupScreen();
3192 }
3193
3194 static void execExitSetup()
3195 {
3196   game_status = GAME_MODE_MAIN;
3197   DrawMainMenu();
3198 }
3199
3200 static void execSaveAndExitSetup()
3201 {
3202   SaveSetup();
3203   execExitSetup();
3204 }
3205
3206 static struct TokenInfo setup_info_main[] =
3207 {
3208   { TYPE_ENTER_MENU,    execSetupGame,          "Game & Menu"           },
3209   { TYPE_ENTER_MENU,    execSetupEditor,        "Editor"                },
3210   { TYPE_ENTER_MENU,    execSetupGraphics,      "Graphics"              },
3211   { TYPE_ENTER_MENU,    execSetupSound,         "Sound & Music"         },
3212   { TYPE_ENTER_MENU,    execSetupArtwork,       "Custom Artwork"        },
3213   { TYPE_ENTER_MENU,    execSetupInput,         "Input Devices"         },
3214   { TYPE_ENTER_MENU,    execSetupShortcut1,     "Key Shortcuts 1"       },
3215   { TYPE_ENTER_MENU,    execSetupShortcut2,     "Key Shortcuts 2"       },
3216   { TYPE_EMPTY,         NULL,                   ""                      },
3217   { TYPE_LEAVE_MENU,    execExitSetup,          "Exit"                  },
3218   { TYPE_LEAVE_MENU,    execSaveAndExitSetup,   "Save and Exit"         },
3219
3220   { 0,                  NULL,                   NULL                    }
3221 };
3222
3223 static struct TokenInfo setup_info_game[] =
3224 {
3225   { TYPE_SWITCH,        &setup.team_mode,       "Team-Mode (Multi-Player):" },
3226   { TYPE_YES_NO,        &setup.input_on_focus,  "Only Move Focussed Player:" },
3227   { TYPE_SWITCH,        &setup.handicap,        "Handicap:"             },
3228   { TYPE_SWITCH,        &setup.skip_levels,     "Skip Unsolved Levels:" },
3229   { TYPE_SWITCH,        &setup.time_limit,      "Time Limit:"           },
3230   { TYPE_SWITCH,        &setup.autorecord,      "Auto-Record Tapes:"    },
3231   { TYPE_EMPTY,         NULL,                   ""                      },
3232   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3233
3234   { 0,                  NULL,                   NULL                    }
3235 };
3236
3237 static struct TokenInfo setup_info_editor[] =
3238 {
3239 #if 0
3240   { TYPE_SWITCH,        &setup.editor.el_boulderdash,   "Boulder Dash:" },
3241   { TYPE_SWITCH,        &setup.editor.el_emerald_mine,  "Emerald Mine:" },
3242   { TYPE_SWITCH, &setup.editor.el_emerald_mine_club,    "Emerald Mine Club:" },
3243   { TYPE_SWITCH,        &setup.editor.el_more,          "Rocks'n'Diamonds:" },
3244   { TYPE_SWITCH,        &setup.editor.el_sokoban,       "Sokoban:"      },
3245   { TYPE_SWITCH,        &setup.editor.el_supaplex,      "Supaplex:"     },
3246   { TYPE_SWITCH,        &setup.editor.el_diamond_caves, "Diamond Caves II:" },
3247   { TYPE_SWITCH,        &setup.editor.el_dx_boulderdash,"DX-Boulderdash:" },
3248 #endif
3249   { TYPE_SWITCH,        &setup.editor.el_chars,         "Text Characters:" },
3250   { TYPE_SWITCH,        &setup.editor.el_custom,  "Custom & Group Elements:" },
3251 #if 0
3252   { TYPE_SWITCH,        &setup.editor.el_headlines,     "Headlines:"    },
3253 #endif
3254   { TYPE_SWITCH, &setup.editor.el_user_defined, "User defined element list:" },
3255   { TYPE_SWITCH,        &setup.editor.el_dynamic,  "Dynamic level elements:" },
3256   { TYPE_EMPTY,         NULL,                   ""                      },
3257 #if 0
3258   { TYPE_SWITCH,        &setup.editor.el_by_game,   "Show elements by game:" },
3259   { TYPE_SWITCH,        &setup.editor.el_by_type,   "Show elements by type:" },
3260   { TYPE_EMPTY,         NULL,                   ""                      },
3261 #endif
3262   { TYPE_SWITCH, &setup.editor.show_element_token,      "Show element token:" },
3263   { TYPE_EMPTY,         NULL,                   ""                      },
3264   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3265
3266   { 0,                  NULL,                   NULL                    }
3267 };
3268
3269 static struct TokenInfo setup_info_graphics[] =
3270 {
3271   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen:"           },
3272   { TYPE_ENTER_LIST,    execSetupChooseScreenMode, "Fullscreen Mode:"   },
3273   { TYPE_STRING,        &screen_mode_text,      ""                      },
3274   { TYPE_SWITCH,        &setup.scroll_delay,    "Delayed Scrolling:"    },
3275 #if 0
3276   { TYPE_SWITCH,        &setup.soft_scrolling,  "Soft Scrolling:"       },
3277   { TYPE_SWITCH,        &setup.double_buffering,"Double-Buffering:"     },
3278 #endif
3279   { TYPE_SWITCH,        &setup.fade_screens,    "Fade Screens:"         },
3280   { TYPE_SWITCH,        &setup.quick_switch,    "Quick Player Focus Switch:" },
3281   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Menu Doors:"     },
3282   { TYPE_SWITCH,        &setup.show_titlescreen,"Show Title Screens:"   },
3283   { TYPE_SWITCH,        &setup.toons,           "Show Toons:"           },
3284   { TYPE_ECS_AGA,       &setup.prefer_aga_graphics,"EMC graphics preference:" },
3285   { TYPE_EMPTY,         NULL,                   ""                      },
3286   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3287
3288   { 0,                  NULL,                   NULL                    }
3289 };
3290
3291 static struct TokenInfo setup_info_sound[] =
3292 {
3293   { TYPE_SWITCH,        &setup.sound_simple,    "Sound Effects (Normal):"  },
3294   { TYPE_SWITCH,        &setup.sound_loops,     "Sound Effects (Looping):" },
3295   { TYPE_SWITCH,        &setup.sound_music,     "Music:"                },
3296   { TYPE_EMPTY,         NULL,                   ""                      },
3297   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3298
3299   { 0,                  NULL,                   NULL                    }
3300 };
3301
3302 static struct TokenInfo setup_info_artwork[] =
3303 {
3304   { TYPE_ENTER_LIST,    execSetupChooseGraphics,"Custom Graphics:"      },
3305   { TYPE_STRING,        &graphics_set_name,     ""                      },
3306   { TYPE_ENTER_LIST,    execSetupChooseSounds,  "Custom Sounds:"        },
3307   { TYPE_STRING,        &sounds_set_name,       ""                      },
3308   { TYPE_ENTER_LIST,    execSetupChooseMusic,   "Custom Music:"         },
3309   { TYPE_STRING,        &music_set_name,        ""                      },
3310   { TYPE_EMPTY,         NULL,                   ""                      },
3311 #if 1
3312   { TYPE_YES_NO, &setup.override_level_graphics,"Override Level Graphics:" },
3313   { TYPE_YES_NO, &setup.override_level_sounds,  "Override Level Sounds:"   },
3314   { TYPE_YES_NO, &setup.override_level_music,   "Override Level Music:"    },
3315 #else
3316   { TYPE_STRING,        NULL,                   "Override Level Artwork:"},
3317   { TYPE_YES_NO,        &setup.override_level_graphics, "Graphics:"     },
3318   { TYPE_YES_NO,        &setup.override_level_sounds,   "Sounds:"       },
3319   { TYPE_YES_NO,        &setup.override_level_music,    "Music:"        },
3320 #endif
3321   { TYPE_EMPTY,         NULL,                   ""                      },
3322   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3323
3324   { 0,                  NULL,                   NULL                    }
3325 };
3326
3327 static struct TokenInfo setup_info_input[] =
3328 {
3329   { TYPE_SWITCH,        NULL,                   "Player:"               },
3330   { TYPE_SWITCH,        NULL,                   "Device:"               },
3331   { TYPE_ENTER_MENU,    NULL,                   ""                      },
3332   { TYPE_EMPTY,         NULL,                   ""                      },
3333   { TYPE_EMPTY,         NULL,                   ""                      },
3334   { TYPE_EMPTY,         NULL,                   ""                      },
3335   { TYPE_EMPTY,         NULL,                   ""                      },
3336   { TYPE_EMPTY,         NULL,                   ""                      },
3337   { TYPE_EMPTY,         NULL,                   ""                      },
3338   { TYPE_EMPTY,         NULL,                   ""                      },
3339   { TYPE_EMPTY,         NULL,                   ""                      },
3340   { TYPE_EMPTY,         NULL,                   ""                      },
3341   { TYPE_EMPTY,         NULL,                   ""                      },
3342   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3343
3344   { 0,                  NULL,                   NULL                    }
3345 };
3346
3347 static struct TokenInfo setup_info_shortcut_1[] =
3348 {
3349   { TYPE_KEYTEXT,       NULL,           "Quick Save Game to Tape:",     },
3350   { TYPE_KEY,           &setup.shortcut.save_game, ""                   },
3351   { TYPE_KEYTEXT,       NULL,           "Quick Load Game from Tape:",   },
3352   { TYPE_KEY,           &setup.shortcut.load_game, ""                   },
3353   { TYPE_KEYTEXT,       NULL,           "Start Game & Toggle Pause:",   },
3354   { TYPE_KEY,           &setup.shortcut.toggle_pause, ""                },
3355   { TYPE_EMPTY,         NULL,                   ""                      },
3356   { TYPE_YES_NO,        &setup.ask_on_escape,   "Ask on 'Esc' Key:"     },
3357   { TYPE_YES_NO, &setup.ask_on_escape_editor,   "Ask on 'Esc' Key (Editor):" },
3358   { TYPE_EMPTY,         NULL,                   ""                      },
3359   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3360
3361   { 0,                  NULL,                   NULL                    }
3362 };
3363
3364 static struct TokenInfo setup_info_shortcut_2[] =
3365 {
3366   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 1:",       },
3367   { TYPE_KEY,           &setup.shortcut.focus_player[0], ""             },
3368   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 2:",       },
3369   { TYPE_KEY,           &setup.shortcut.focus_player[1], ""             },
3370   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 3:",       },
3371   { TYPE_KEY,           &setup.shortcut.focus_player[2], ""             },
3372   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 4:",       },
3373   { TYPE_KEY,           &setup.shortcut.focus_player[3], ""             },
3374   { TYPE_KEYTEXT,       NULL,           "Set Focus to All Players:",    },
3375   { TYPE_KEY,           &setup.shortcut.focus_player_all, ""            },
3376   { TYPE_EMPTY,         NULL,                   ""                      },
3377   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3378
3379   { 0,                  NULL,                   NULL                    }
3380 };
3381
3382 static Key getSetupKey()
3383 {
3384   Key key = KSYM_UNDEFINED;
3385   boolean got_key_event = FALSE;
3386
3387   while (!got_key_event)
3388   {
3389     if (PendingEvent())         /* got event */
3390     {
3391       Event event;
3392
3393       NextEvent(&event);
3394
3395       switch (event.type)
3396       {
3397         case EVENT_KEYPRESS:
3398           {
3399             key = GetEventKey((KeyEvent *)&event, TRUE);
3400
3401             /* press 'Escape' or 'Enter' to keep the existing key binding */
3402             if (key == KSYM_Escape || key == KSYM_Return)
3403               key = KSYM_UNDEFINED;     /* keep old value */
3404
3405             got_key_event = TRUE;
3406           }
3407           break;
3408
3409         case EVENT_KEYRELEASE:
3410           key_joystick_mapping = 0;
3411           break;
3412
3413         default:
3414           HandleOtherEvents(&event);
3415           break;
3416       }
3417     }
3418
3419     DoAnimation();
3420     BackToFront();
3421
3422     /* don't eat all CPU time */
3423     Delay(10);
3424   }
3425
3426   return key;
3427 }
3428
3429 static int getSetupTextFont(int type)
3430 {
3431   if (type & (TYPE_SWITCH |
3432               TYPE_YES_NO |
3433               TYPE_STRING |
3434               TYPE_ECS_AGA |
3435               TYPE_KEYTEXT |
3436               TYPE_ENTER_LIST))
3437     return FONT_MENU_2;
3438   else
3439     return FONT_MENU_1;
3440 }
3441
3442 static int getSetupValueFont(int type, void *value)
3443 {
3444   if (type & TYPE_KEY)
3445     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
3446   else if (type & TYPE_STRING)
3447     return FONT_VALUE_2;
3448   else if (type & TYPE_ECS_AGA)
3449     return FONT_VALUE_1;
3450   else if (type & TYPE_BOOLEAN_STYLE)
3451     return (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
3452   else
3453     return FONT_VALUE_1;
3454 }
3455
3456 static void drawSetupValue(int pos)
3457 {
3458   boolean font_draw_xoffset_modified = FALSE;
3459   int font_draw_xoffset_old = -1;
3460   int xpos = MENU_SCREEN_VALUE_XPOS;
3461   int ypos = MENU_SCREEN_START_YPOS + pos;
3462   int startx = mSX + xpos * 32;
3463   int starty = mSY + ypos * 32;
3464   int font_nr, font_width;
3465   int type = setup_info[pos].type;
3466   void *value = setup_info[pos].value;
3467   char *value_string = getSetupValue(type, value);
3468   int i;
3469
3470   if (value_string == NULL)
3471     return;
3472
3473   if (type & TYPE_KEY)
3474   {
3475     xpos = MENU_SCREEN_START_XPOS;
3476
3477     if (type & TYPE_QUERY)
3478     {
3479       value_string = "<press key>";
3480     }
3481   }
3482   else if (type & TYPE_STRING)
3483   {
3484     int max_value_len = (SCR_FIELDX - 2) * 2;
3485
3486     xpos = MENU_SCREEN_START_XPOS;
3487
3488     if (strlen(value_string) > max_value_len)
3489       value_string[max_value_len] = '\0';
3490   }
3491
3492   startx = mSX + xpos * 32;
3493   starty = mSY + ypos * 32;
3494   font_nr = getSetupValueFont(type, value);
3495   font_width = getFontWidth(font_nr);
3496
3497   /* downward compatibility correction for Juergen Bonhagen's menu settings */
3498   if (setup_mode != SETUP_MODE_INPUT)
3499   {
3500     int check_font_nr = FONT_OPTION_ON; /* known font that needs correction */
3501     int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
3502     int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset;
3503     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
3504     int text_font_nr = getSetupTextFont(FONT_MENU_2);
3505     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
3506     int text_width = MAX_MENU_TEXT_LENGTH_MEDIUM * getFontWidth(text_font_nr);
3507     boolean correct_font_draw_xoffset = FALSE;
3508
3509     if (xpos == MENU_SCREEN_START_XPOS &&
3510         startx + font1_xoffset < text_startx + text_font_xoffset)
3511       correct_font_draw_xoffset = TRUE;
3512
3513     if (xpos == MENU_SCREEN_VALUE_XPOS &&
3514         startx + font2_xoffset < text_startx + text_width + text_font_xoffset)
3515       correct_font_draw_xoffset = TRUE;
3516
3517     /* check if setup value would overlap with setup text when printed */
3518     /* (this can happen for extreme/wrong values for font draw offset) */
3519     if (correct_font_draw_xoffset)
3520     {
3521       font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset;
3522       font_draw_xoffset_modified = TRUE;
3523
3524       if (type & TYPE_KEY)
3525         getFontBitmapInfo(font_nr)->draw_xoffset += 2 * getFontWidth(font_nr);
3526       else if (!(type & TYPE_STRING))
3527         getFontBitmapInfo(font_nr)->draw_xoffset = text_font_xoffset + 20 -
3528           MAX_MENU_TEXT_LENGTH_MEDIUM * (16 - getFontWidth(text_font_nr));
3529     }
3530   }
3531
3532   for (i = 0; i <= MENU_SCREEN_MAX_XPOS - xpos; i++)
3533     DrawText(startx + i * font_width, starty, " ", font_nr);
3534
3535   DrawText(startx, starty, value_string, font_nr);
3536
3537   if (font_draw_xoffset_modified)
3538     getFontBitmapInfo(font_nr)->draw_xoffset = font_draw_xoffset_old;
3539 }
3540
3541 static void changeSetupValue(int pos)
3542 {
3543   if (setup_info[pos].type & TYPE_BOOLEAN_STYLE)
3544   {
3545     *(boolean *)setup_info[pos].value ^= TRUE;
3546   }
3547   else if (setup_info[pos].type & TYPE_KEY)
3548   {
3549     Key key;
3550
3551     setup_info[pos].type |= TYPE_QUERY;
3552     drawSetupValue(pos);
3553     setup_info[pos].type &= ~TYPE_QUERY;
3554
3555     key = getSetupKey();
3556     if (key != KSYM_UNDEFINED)
3557       *(Key *)setup_info[pos].value = key;
3558   }
3559
3560   drawSetupValue(pos);
3561 }
3562
3563 static void DrawCursorAndText_Setup(int pos, boolean active)
3564 {
3565   int xpos = MENU_SCREEN_START_XPOS;
3566   int ypos = MENU_SCREEN_START_YPOS + pos;
3567   int font_nr = getSetupTextFont(setup_info[pos].type);
3568
3569   if (setup_info == setup_info_input)
3570     font_nr = FONT_MENU_1;
3571
3572   if (active)
3573     font_nr = FONT_ACTIVE(font_nr);
3574
3575   DrawText(mSX + xpos * 32, mSY + ypos * 32, setup_info[pos].text, font_nr);
3576
3577   if (setup_info[pos].type & ~TYPE_SKIP_ENTRY)
3578     drawCursor(pos, active);
3579 }
3580
3581 static void DrawSetupScreen_Generic()
3582 {
3583   char *title_string = NULL;
3584   int i;
3585
3586   UnmapAllGadgets();
3587   CloseDoor(DOOR_CLOSE_2);
3588
3589   ClearWindow();
3590
3591   if (setup_mode == SETUP_MODE_MAIN)
3592   {
3593     setup_info = setup_info_main;
3594     title_string = "Setup";
3595   }
3596   else if (setup_mode == SETUP_MODE_GAME)
3597   {
3598     setup_info = setup_info_game;
3599     title_string = "Setup Game";
3600   }
3601   else if (setup_mode == SETUP_MODE_EDITOR)
3602   {
3603     setup_info = setup_info_editor;
3604     title_string = "Setup Editor";
3605   }
3606   else if (setup_mode == SETUP_MODE_GRAPHICS)
3607   {
3608     setup_info = setup_info_graphics;
3609     title_string = "Setup Graphics";
3610   }
3611   else if (setup_mode == SETUP_MODE_SOUND)
3612   {
3613     setup_info = setup_info_sound;
3614     title_string = "Setup Sound";
3615   }
3616   else if (setup_mode == SETUP_MODE_ARTWORK)
3617   {
3618     setup_info = setup_info_artwork;
3619     title_string = "Custom Artwork";
3620   }
3621   else if (setup_mode == SETUP_MODE_SHORTCUT_1)
3622   {
3623     setup_info = setup_info_shortcut_1;
3624     title_string = "Setup Shortcuts";
3625   }
3626   else if (setup_mode == SETUP_MODE_SHORTCUT_2)
3627   {
3628     setup_info = setup_info_shortcut_2;
3629     title_string = "Setup Shortcuts";
3630   }
3631
3632   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
3633
3634   num_setup_info = 0;
3635   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
3636   {
3637     void *value_ptr = setup_info[i].value;
3638 #if 1
3639 #else
3640     int xpos = MENU_SCREEN_START_XPOS;
3641     int ypos = MENU_SCREEN_START_YPOS + i;
3642     int font_nr;
3643 #endif
3644
3645     /* set some entries to "unchangeable" according to other variables */
3646     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
3647         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
3648         (value_ptr == &setup.sound_music  && !audio.music_available) ||
3649         (value_ptr == &setup.fullscreen   && !video.fullscreen_available) ||
3650         (value_ptr == &screen_mode_text   && !video.fullscreen_available))
3651       setup_info[i].type |= TYPE_GHOSTED;
3652
3653     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
3654       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
3655     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
3656       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
3657     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
3658       initCursor(i, IMG_MENU_BUTTON);
3659
3660 #if 1
3661     DrawCursorAndText_Setup(i, FALSE);
3662 #else
3663     font_nr = getSetupTextFont(setup_info[i].type);
3664
3665     DrawText(mSX + xpos * 32, mSY + ypos * 32, setup_info[i].text, font_nr);
3666 #endif
3667
3668     if (setup_info[i].type & TYPE_VALUE)
3669       drawSetupValue(i);
3670
3671     num_setup_info++;
3672   }
3673
3674 #if 0
3675   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3676                     "Joysticks deactivated in setup menu");
3677 #endif
3678
3679   FadeToFront();
3680   InitAnimation();
3681   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
3682 }
3683
3684 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
3685 {
3686   static int choice_store[MAX_SETUP_MODES];
3687   int choice = choice_store[setup_mode];        /* always starts with 0 */
3688   int x = 0;
3689   int y = choice;
3690
3691   if (button == MB_MENU_INITIALIZE)
3692   {
3693     /* advance to first valid menu entry */
3694     while (choice < num_setup_info &&
3695            setup_info[choice].type & TYPE_SKIP_ENTRY)
3696       choice++;
3697     choice_store[setup_mode] = choice;
3698
3699 #if 1
3700     DrawCursorAndText_Setup(choice, TRUE);
3701 #else
3702     drawCursor(choice, TRUE);
3703 #endif
3704
3705     return;
3706   }
3707   else if (button == MB_MENU_LEAVE)
3708   {
3709     for (y = 0; y < num_setup_info; y++)
3710     {
3711       if (setup_info[y].type & TYPE_LEAVE_MENU)
3712       {
3713         void (*menu_callback_function)(void) = setup_info[y].value;
3714
3715         menu_callback_function();
3716
3717         break;  /* absolutely needed because function changes 'setup_info'! */
3718       }
3719     }
3720
3721     return;
3722   }
3723
3724   if (mx || my)         /* mouse input */
3725   {
3726     x = (mx - mSX) / 32;
3727     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3728   }
3729   else if (dx || dy)    /* keyboard input */
3730   {
3731     if (dx)
3732     {
3733       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
3734
3735       if (setup_info[choice].type & menu_navigation_type ||
3736           setup_info[choice].type & TYPE_BOOLEAN_STYLE)
3737         button = MB_MENU_CHOICE;
3738     }
3739     else if (dy)
3740       y = choice + dy;
3741
3742     /* jump to next non-empty menu entry (up or down) */
3743     while (y > 0 && y < num_setup_info - 1 &&
3744            setup_info[y].type & TYPE_SKIP_ENTRY)
3745       y += dy;
3746   }
3747
3748   if (IN_VIS_FIELD(x, y) && y >= 0 && y < num_setup_info)
3749   {
3750     if (button)
3751     {
3752       if (y != choice && setup_info[y].type & ~TYPE_SKIP_ENTRY)
3753       {
3754 #if 1
3755         DrawCursorAndText_Setup(choice, FALSE);
3756         DrawCursorAndText_Setup(y, TRUE);
3757 #else
3758         drawCursor(choice, FALSE);
3759         drawCursor(y, TRUE);
3760 #endif
3761
3762         choice = choice_store[setup_mode] = y;
3763       }
3764     }
3765     else if (!(setup_info[y].type & TYPE_GHOSTED))
3766     {
3767       /* when selecting key headline, execute function for key value change */
3768       if (setup_info[y].type & TYPE_KEYTEXT &&
3769           setup_info[y + 1].type & TYPE_KEY)
3770         y++;
3771
3772       /* when selecting string value, execute function for list selection */
3773       if (setup_info[y].type & TYPE_STRING && y > 0 &&
3774           setup_info[y - 1].type & TYPE_ENTER_LIST)
3775         y--;
3776
3777       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE)
3778       {
3779         void (*menu_callback_function)(void) = setup_info[y].value;
3780
3781         menu_callback_function();
3782       }
3783       else
3784       {
3785         if (setup_info[y].type & TYPE_VALUE)
3786           changeSetupValue(y);
3787       }
3788     }
3789   }
3790 }
3791
3792 void DrawSetupScreen_Input()
3793 {
3794 #if 1
3795   int i;
3796 #endif
3797
3798   ClearWindow();
3799
3800 #if 1
3801   setup_info = setup_info_input;
3802 #endif
3803
3804   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Setup Input");
3805
3806 #if 1
3807   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
3808   {
3809     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
3810       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
3811     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
3812       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
3813     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
3814       initCursor(i, IMG_MENU_BUTTON);
3815
3816     DrawCursorAndText_Setup(i, FALSE);
3817   }
3818 #else
3819   initCursor(0,  IMG_MENU_BUTTON);
3820   initCursor(1,  IMG_MENU_BUTTON);
3821   initCursor(2,  IMG_MENU_BUTTON_ENTER_MENU);
3822   initCursor(13, IMG_MENU_BUTTON_LEAVE_MENU);
3823
3824   DrawText(mSX + 32, mSY +  2 * 32, "Player:", FONT_MENU_1);
3825   DrawText(mSX + 32, mSY +  3 * 32, "Device:", FONT_MENU_1);
3826   DrawText(mSX + 32, mSY + 15 * 32, "Back",   FONT_MENU_1);
3827 #endif
3828
3829 #if 0
3830   DeactivateJoystickForCalibration();
3831 #endif
3832 #if 1
3833   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3834                     "Joysticks deactivated on this screen");
3835 #endif
3836
3837   /* create gadgets for setup input menu screen */
3838   FreeScreenGadgets();
3839   CreateScreenGadgets();
3840
3841   /* map gadgets for setup input menu screen */
3842   MapScreenMenuGadgets(SCREEN_MASK_INPUT);
3843
3844   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
3845   FadeToFront();
3846   InitAnimation();
3847 }
3848
3849 static void setJoystickDeviceToNr(char *device_name, int device_nr)
3850 {
3851   if (device_name == NULL)
3852     return;
3853
3854   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
3855     device_nr = 0;
3856
3857   if (strlen(device_name) > 1)
3858   {
3859     char c1 = device_name[strlen(device_name) - 1];
3860     char c2 = device_name[strlen(device_name) - 2];
3861
3862     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
3863       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
3864   }
3865   else
3866     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
3867             strlen(device_name));
3868 }
3869
3870 static void drawPlayerSetupInputInfo(int player_nr, boolean active)
3871 {
3872   int i;
3873   static struct SetupKeyboardInfo custom_key;
3874   static struct
3875   {
3876     Key *key;
3877     char *text;
3878   } custom[] =
3879   {
3880     { &custom_key.left,  "Joystick Left"  },
3881     { &custom_key.right, "Joystick Right" },
3882     { &custom_key.up,    "Joystick Up"    },
3883     { &custom_key.down,  "Joystick Down"  },
3884     { &custom_key.snap,  "Button 1"       },
3885     { &custom_key.drop,  "Button 2"       }
3886   };
3887   static char *joystick_name[MAX_PLAYERS] =
3888   {
3889     "Joystick1",
3890     "Joystick2",
3891     "Joystick3",
3892     "Joystick4"
3893   };
3894   int text_font_nr = (active ? FONT_MENU_1_ACTIVE : FONT_MENU_1);
3895
3896   InitJoysticks();
3897
3898   custom_key = setup.input[player_nr].key;
3899
3900   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
3901            FONT_INPUT_1_ACTIVE);
3902
3903   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3904                              TILEX, TILEY);
3905   DrawGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3906                          PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
3907
3908   if (setup.input[player_nr].use_joystick)
3909   {
3910     char *device_name = setup.input[player_nr].joy.device_name;
3911     char *text = joystick_name[getJoystickNrFromDeviceName(device_name)];
3912     int font_nr = (joystick.fd[player_nr] < 0 ? FONT_VALUE_OLD : FONT_VALUE_1);
3913
3914     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
3915     DrawText(mSX + 32, mSY + 4 * 32, "Calibrate", text_font_nr);
3916   }
3917   else
3918   {
3919     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", FONT_VALUE_1);
3920     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", text_font_nr);
3921   }
3922
3923   DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", FONT_MENU_1);
3924
3925   drawCursorXY(1, 4, IMG_MENU_BUTTON_LEFT);
3926   drawCursorXY(1, 5, IMG_MENU_BUTTON_RIGHT);
3927   drawCursorXY(1, 6, IMG_MENU_BUTTON_UP);
3928   drawCursorXY(1, 7, IMG_MENU_BUTTON_DOWN);
3929
3930   DrawText(mSX + 2 * 32, mSY +  6 * 32, ":", FONT_VALUE_OLD);
3931   DrawText(mSX + 2 * 32, mSY +  7 * 32, ":", FONT_VALUE_OLD);
3932   DrawText(mSX + 2 * 32, mSY +  8 * 32, ":", FONT_VALUE_OLD);
3933   DrawText(mSX + 2 * 32, mSY +  9 * 32, ":", FONT_VALUE_OLD);
3934   DrawText(mSX + 1 * 32, mSY + 10 * 32, "Snap Field:", FONT_VALUE_OLD);
3935   DrawText(mSX + 1 * 32, mSY + 12 * 32, "Drop Element:", FONT_VALUE_OLD);
3936
3937   for (i = 0; i < 6; i++)
3938   {
3939     int ypos = 6 + i + (i > 3 ? i-3 : 0);
3940
3941     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3942              "              ", FONT_VALUE_1);
3943     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3944              (setup.input[player_nr].use_joystick ?
3945               custom[i].text :
3946               getKeyNameFromKey(*custom[i].key)), FONT_VALUE_1);
3947   }
3948 }
3949
3950 static int input_player_nr = 0;
3951
3952 void HandleSetupScreen_Input_Player(int step, int direction)
3953 {
3954   int old_player_nr = input_player_nr;
3955   int new_player_nr;
3956
3957   new_player_nr = old_player_nr + step * direction;
3958   if (new_player_nr < 0)
3959     new_player_nr = 0;
3960   if (new_player_nr > MAX_PLAYERS - 1)
3961     new_player_nr = MAX_PLAYERS - 1;
3962
3963   if (new_player_nr != old_player_nr)
3964   {
3965     input_player_nr = new_player_nr;
3966
3967     drawPlayerSetupInputInfo(input_player_nr, FALSE);
3968   }
3969 }
3970
3971 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
3972 {
3973   static int choice = 0;
3974   int x = 0;
3975   int y = choice;
3976   int pos_start  = SETUPINPUT_SCREEN_POS_START;
3977   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
3978   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
3979   int pos_end    = SETUPINPUT_SCREEN_POS_END;
3980
3981   if (button == MB_MENU_INITIALIZE)
3982   {
3983     drawPlayerSetupInputInfo(input_player_nr, (choice == 2));
3984
3985 #if 1
3986     DrawCursorAndText_Setup(choice, TRUE);
3987 #else
3988     drawCursor(choice, TRUE);
3989 #endif
3990
3991     return;
3992   }
3993   else if (button == MB_MENU_LEAVE)
3994   {
3995     setup_mode = SETUP_MODE_MAIN;
3996     DrawSetupScreen();
3997     InitJoysticks();
3998
3999     return;
4000   }
4001
4002   if (mx || my)         /* mouse input */
4003   {
4004     x = (mx - mSX) / 32;
4005     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
4006   }
4007   else if (dx || dy)    /* keyboard input */
4008   {
4009     if (dx && choice == 0)
4010       x = (dx < 0 ? 10 : 12);
4011     else if ((dx && choice == 1) ||
4012              (dx == +1 && choice == 2) ||
4013              (dx == -1 && choice == pos_end))
4014       button = MB_MENU_CHOICE;
4015     else if (dy)
4016       y = choice + dy;
4017
4018     if (y >= pos_empty1 && y <= pos_empty2)
4019       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
4020   }
4021
4022   if (y == 0 && dx != 0 && button)
4023   {
4024     HandleSetupScreen_Input_Player(1, dx < 0 ? -1 : +1);
4025   }
4026   else if (IN_VIS_FIELD(x, y) &&
4027            y >= pos_start && y <= pos_end &&
4028            !(y >= pos_empty1 && y <= pos_empty2))
4029   {
4030     if (button)
4031     {
4032       if (y != choice)
4033       {
4034 #if 1
4035         DrawCursorAndText_Setup(choice, FALSE);
4036         DrawCursorAndText_Setup(y, TRUE);
4037
4038         drawPlayerSetupInputInfo(input_player_nr, (y == 2));
4039 #else
4040         drawCursor(choice, FALSE);
4041         drawCursor(y, TRUE);
4042 #endif
4043
4044         choice = y;
4045       }
4046     }
4047     else
4048     {
4049       if (y == 1)
4050       {
4051         char *device_name = setup.input[input_player_nr].joy.device_name;
4052
4053         if (!setup.input[input_player_nr].use_joystick)
4054         {
4055           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
4056
4057           setJoystickDeviceToNr(device_name, new_device_nr);
4058           setup.input[input_player_nr].use_joystick = TRUE;
4059         }
4060         else
4061         {
4062           int device_nr = getJoystickNrFromDeviceName(device_name);
4063           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
4064
4065           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
4066             setup.input[input_player_nr].use_joystick = FALSE;
4067           else
4068             setJoystickDeviceToNr(device_name, new_device_nr);
4069         }
4070
4071         drawPlayerSetupInputInfo(input_player_nr, FALSE);
4072       }
4073       else if (y == 2)
4074       {
4075         if (setup.input[input_player_nr].use_joystick)
4076         {
4077           InitJoysticks();
4078           CalibrateJoystick(input_player_nr);
4079         }
4080         else
4081           CustomizeKeyboard(input_player_nr);
4082       }
4083       else if (y == pos_end)
4084       {
4085         InitJoysticks();
4086
4087         setup_mode = SETUP_MODE_MAIN;
4088         DrawSetupScreen();
4089       }
4090     }
4091   }
4092 }
4093
4094 void CustomizeKeyboard(int player_nr)
4095 {
4096   int i;
4097   int step_nr;
4098   boolean finished = FALSE;
4099   static struct SetupKeyboardInfo custom_key;
4100   static struct
4101   {
4102     Key *key;
4103     char *text;
4104   } customize_step[] =
4105   {
4106     { &custom_key.left,  "Move Left"    },
4107     { &custom_key.right, "Move Right"   },
4108     { &custom_key.up,    "Move Up"      },
4109     { &custom_key.down,  "Move Down"    },
4110     { &custom_key.snap,  "Snap Field"   },
4111     { &custom_key.drop,  "Drop Element" }
4112   };
4113
4114   /* read existing key bindings from player setup */
4115   custom_key = setup.input[player_nr].key;
4116
4117   ClearWindow();
4118
4119   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
4120
4121   BackToFront();
4122   InitAnimation();
4123
4124   step_nr = 0;
4125   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
4126            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
4127   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
4128            "Key:", FONT_INPUT_1_ACTIVE);
4129   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4130            getKeyNameFromKey(*customize_step[step_nr].key), FONT_VALUE_OLD);
4131
4132   while (!finished)
4133   {
4134     if (PendingEvent())         /* got event */
4135     {
4136       Event event;
4137
4138       NextEvent(&event);
4139
4140       switch (event.type)
4141       {
4142         case EVENT_KEYPRESS:
4143           {
4144             Key key = GetEventKey((KeyEvent *)&event, FALSE);
4145
4146             if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
4147             {
4148               finished = TRUE;
4149               break;
4150             }
4151
4152             /* all keys configured -- wait for "Escape" or "Return" key */
4153             if (step_nr == 6)
4154               break;
4155
4156             /* press 'Enter' to keep the existing key binding */
4157             if (key == KSYM_Return)
4158               key = *customize_step[step_nr].key;
4159
4160             /* check if key already used */
4161             for (i = 0; i < step_nr; i++)
4162               if (*customize_step[i].key == key)
4163                 break;
4164             if (i < step_nr)
4165               break;
4166
4167             /* got new key binding */
4168             *customize_step[step_nr].key = key;
4169             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4170                      "             ", FONT_VALUE_1);
4171             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4172                      getKeyNameFromKey(key), FONT_VALUE_1);
4173             step_nr++;
4174
4175             /* un-highlight last query */
4176             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
4177                      customize_step[step_nr - 1].text, FONT_MENU_1);
4178             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
4179                      "Key:", FONT_MENU_1);
4180
4181             /* press 'Enter' to leave */
4182             if (step_nr == 6)
4183             {
4184               DrawText(mSX + 16, mSY + 15 * 32 + 16,
4185                        "Press Enter", FONT_TITLE_1);
4186               break;
4187             }
4188
4189             /* query next key binding */
4190             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
4191                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
4192             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
4193                      "Key:", FONT_INPUT_1_ACTIVE);
4194             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4195                      getKeyNameFromKey(*customize_step[step_nr].key),
4196                      FONT_VALUE_OLD);
4197           }
4198           break;
4199
4200         case EVENT_KEYRELEASE:
4201           key_joystick_mapping = 0;
4202           break;
4203
4204         default:
4205           HandleOtherEvents(&event);
4206           break;
4207       }
4208     }
4209
4210     DoAnimation();
4211     BackToFront();
4212
4213     /* don't eat all CPU time */
4214     Delay(10);
4215   }
4216
4217   /* write new key bindings back to player setup */
4218   setup.input[player_nr].key = custom_key;
4219
4220   StopAnimation();
4221   DrawSetupScreen_Input();
4222 }
4223
4224 static boolean CalibrateJoystickMain(int player_nr)
4225 {
4226   int new_joystick_xleft = JOYSTICK_XMIDDLE;
4227   int new_joystick_xright = JOYSTICK_XMIDDLE;
4228   int new_joystick_yupper = JOYSTICK_YMIDDLE;
4229   int new_joystick_ylower = JOYSTICK_YMIDDLE;
4230   int new_joystick_xmiddle, new_joystick_ymiddle;
4231
4232   int joystick_fd = joystick.fd[player_nr];
4233   int x, y, last_x, last_y, xpos = 8, ypos = 3;
4234   boolean check[3][3];
4235   int check_remaining = 3 * 3;
4236   int joy_x, joy_y;
4237   int joy_value;
4238   int result = -1;
4239
4240   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
4241     return FALSE;
4242
4243   if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
4244     return FALSE;
4245
4246   ClearWindow();
4247
4248   for (y = 0; y < 3; y++)
4249   {
4250     for (x = 0; x < 3; x++)
4251     {
4252       DrawGraphic(xpos + x - 1, ypos + y - 1, IMG_MENU_CALIBRATE_BLUE, 0);
4253       check[x][y] = FALSE;
4254     }
4255   }
4256
4257   DrawTextSCentered(mSY - SY +  6 * 32, FONT_TITLE_1, "Rotate joystick");
4258   DrawTextSCentered(mSY - SY +  7 * 32, FONT_TITLE_1, "in all directions");
4259   DrawTextSCentered(mSY - SY +  9 * 32, FONT_TITLE_1, "if all balls");
4260   DrawTextSCentered(mSY - SY + 10 * 32, FONT_TITLE_1, "are marked,");
4261   DrawTextSCentered(mSY - SY + 11 * 32, FONT_TITLE_1, "center joystick");
4262   DrawTextSCentered(mSY - SY + 12 * 32, FONT_TITLE_1, "and");
4263   DrawTextSCentered(mSY - SY + 13 * 32, FONT_TITLE_1, "press any button!");
4264
4265   joy_value = Joystick(player_nr);
4266   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
4267   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
4268
4269   /* eventually uncalibrated center position (joystick could be uncentered) */
4270   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
4271     return FALSE;
4272
4273   new_joystick_xmiddle = joy_x;
4274   new_joystick_ymiddle = joy_y;
4275
4276   DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_RED, 0);
4277   BackToFront();
4278
4279   while (Joystick(player_nr) & JOY_BUTTON);     /* wait for released button */
4280   InitAnimation();
4281
4282   while (result < 0)
4283   {
4284     if (PendingEvent())         /* got event */
4285     {
4286       Event event;
4287
4288       NextEvent(&event);
4289
4290       switch (event.type)
4291       {
4292         case EVENT_KEYPRESS:
4293           switch (GetEventKey((KeyEvent *)&event, TRUE))
4294           {
4295             case KSYM_Return:
4296               if (check_remaining == 0)
4297                 result = 1;
4298               break;
4299
4300             case KSYM_Escape:
4301               result = 0;
4302               break;
4303
4304             default:
4305               break;
4306           }
4307           break;
4308
4309         case EVENT_KEYRELEASE:
4310           key_joystick_mapping = 0;
4311           break;
4312
4313         default:
4314           HandleOtherEvents(&event);
4315           break;
4316       }
4317     }
4318
4319     if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
4320       return FALSE;
4321
4322     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
4323     new_joystick_xright = MAX(new_joystick_xright, joy_x);
4324     new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
4325     new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
4326
4327     setup.input[player_nr].joy.xleft = new_joystick_xleft;
4328     setup.input[player_nr].joy.yupper = new_joystick_yupper;
4329     setup.input[player_nr].joy.xright = new_joystick_xright;
4330     setup.input[player_nr].joy.ylower = new_joystick_ylower;
4331     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
4332     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
4333
4334     CheckJoystickData();
4335
4336     joy_value = Joystick(player_nr);
4337
4338     if (joy_value & JOY_BUTTON && check_remaining == 0)
4339       result = 1;
4340
4341     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
4342     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
4343
4344     if (x != last_x || y != last_y)
4345     {
4346       DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_YELLOW, 0);
4347       DrawGraphic(xpos + x,      ypos + y,      IMG_MENU_CALIBRATE_RED,    0);
4348
4349       last_x = x;
4350       last_y = y;
4351
4352       if (check_remaining > 0 && !check[x+1][y+1])
4353       {
4354         check[x+1][y+1] = TRUE;
4355         check_remaining--;
4356       }
4357
4358 #if 0
4359 #ifdef DEBUG
4360       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
4361              setup.input[player_nr].joy.xleft,
4362              setup.input[player_nr].joy.xmiddle,
4363              setup.input[player_nr].joy.xright);
4364       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
4365              setup.input[player_nr].joy.yupper,
4366              setup.input[player_nr].joy.ymiddle,
4367              setup.input[player_nr].joy.ylower);
4368 #endif
4369 #endif
4370
4371     }
4372
4373     DoAnimation();
4374     BackToFront();
4375
4376     /* don't eat all CPU time */
4377     Delay(10);
4378   }
4379
4380   /* calibrated center position (joystick should now be centered) */
4381   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
4382     return FALSE;
4383
4384   new_joystick_xmiddle = joy_x;
4385   new_joystick_ymiddle = joy_y;
4386
4387   StopAnimation();
4388
4389 #if 0
4390   DrawSetupScreen_Input();
4391 #endif
4392
4393   /* wait until the last pressed button was released */
4394   while (Joystick(player_nr) & JOY_BUTTON)
4395   {
4396     if (PendingEvent())         /* got event */
4397     {
4398       Event event;
4399
4400       NextEvent(&event);
4401       HandleOtherEvents(&event);
4402
4403       Delay(10);
4404     }
4405   }
4406
4407   return TRUE;
4408 }
4409
4410 void CalibrateJoystick(int player_nr)
4411 {
4412   if (!CalibrateJoystickMain(player_nr))
4413   {
4414     char *device_name = setup.input[player_nr].joy.device_name;
4415     int nr = getJoystickNrFromDeviceName(device_name) + 1;
4416     int xpos = mSX - SX;
4417     int ypos = mSY - SY;
4418
4419     ClearWindow();
4420
4421     DrawTextF(xpos + 16, ypos + 6 * 32, FONT_TITLE_1, "   JOYSTICK %d   ", nr);
4422     DrawTextF(xpos + 16, ypos + 7 * 32, FONT_TITLE_1, " NOT AVAILABLE! ");
4423     BackToFront();
4424
4425     Delay(2000);                /* show error message for a short time */
4426
4427     ClearEventQueue();
4428   }
4429
4430 #if 1
4431   DrawSetupScreen_Input();
4432 #endif
4433 }
4434
4435 void DrawSetupScreen()
4436 {
4437   DeactivateJoystick();
4438
4439   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
4440
4441   if (setup_mode == SETUP_MODE_INPUT)
4442     DrawSetupScreen_Input();
4443   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
4444     DrawChooseTree(&screen_mode_current);
4445   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
4446     DrawChooseTree(&artwork.gfx_current);
4447   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
4448     DrawChooseTree(&artwork.snd_current);
4449   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
4450     DrawChooseTree(&artwork.mus_current);
4451   else
4452     DrawSetupScreen_Generic();
4453
4454   PlayMenuSound();
4455   PlayMenuMusic();
4456 }
4457
4458 void RedrawSetupScreenAfterFullscreenToggle()
4459 {
4460   if (setup_mode == SETUP_MODE_GRAPHICS)
4461     DrawSetupScreen();
4462 }
4463
4464 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
4465 {
4466   if (setup_mode == SETUP_MODE_INPUT)
4467     HandleSetupScreen_Input(mx, my, dx, dy, button);
4468   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
4469     HandleChooseTree(mx, my, dx, dy, button, &screen_mode_current);
4470   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
4471     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
4472   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
4473     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
4474   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
4475     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
4476   else
4477     HandleSetupScreen_Generic(mx, my, dx, dy, button);
4478
4479   DoAnimation();
4480 }
4481
4482 void HandleGameActions()
4483 {
4484   if (game_status != GAME_MODE_PLAYING)
4485     return;
4486
4487   GameActions();        /* main game loop */
4488
4489   if (tape.auto_play && !tape.playing)
4490     AutoPlayTape();     /* continue automatically playing next tape */
4491 }
4492
4493
4494 /* ---------- new screen button stuff -------------------------------------- */
4495
4496 static void getScreenMenuButtonPos(int *x, int *y, int gadget_id)
4497 {
4498   switch (gadget_id)
4499   {
4500 #if 1
4501     case SCREEN_CTRL_ID_PREV_LEVEL:
4502       *x = mSX + menu.main.button.prev_level.x;
4503       *y = mSY + menu.main.button.prev_level.y;
4504       break;
4505
4506     case SCREEN_CTRL_ID_NEXT_LEVEL:
4507       *x = mSX + menu.main.button.next_level.x;
4508       *y = mSY + menu.main.button.next_level.y;
4509       break;
4510 #else
4511     case SCREEN_CTRL_ID_PREV_LEVEL:
4512       *x = mSX + TILEX * getPrevlevelButtonPos();
4513       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
4514       break;
4515
4516     case SCREEN_CTRL_ID_NEXT_LEVEL:
4517       *x = mSX + TILEX * getNextLevelButtonPos();
4518       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
4519       break;
4520 #endif
4521
4522     case SCREEN_CTRL_ID_PREV_PLAYER:
4523       *x = mSX + TILEX * 10;
4524       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
4525       break;
4526
4527     case SCREEN_CTRL_ID_NEXT_PLAYER:
4528       *x = mSX + TILEX * 12;
4529       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
4530       break;
4531
4532     default:
4533       Error(ERR_EXIT, "unknown gadget ID %d", gadget_id);
4534   }
4535 }
4536
4537 static struct
4538 {
4539   int gfx_unpressed, gfx_pressed;
4540   void (*get_gadget_position)(int *, int *, int);
4541   int gadget_id;
4542   int screen_mask;
4543   char *infotext;
4544 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
4545 {
4546   {
4547     IMG_MENU_BUTTON_PREV_LEVEL, IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE,
4548     getScreenMenuButtonPos,
4549     SCREEN_CTRL_ID_PREV_LEVEL,
4550     SCREEN_MASK_MAIN,
4551     "last level"
4552   },
4553   {
4554     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
4555     getScreenMenuButtonPos,
4556     SCREEN_CTRL_ID_NEXT_LEVEL,
4557     SCREEN_MASK_MAIN,
4558     "next level"
4559   },
4560   {
4561     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
4562     getScreenMenuButtonPos,
4563     SCREEN_CTRL_ID_PREV_PLAYER,
4564     SCREEN_MASK_INPUT,
4565     "last player"
4566   },
4567   {
4568     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
4569     getScreenMenuButtonPos,
4570     SCREEN_CTRL_ID_NEXT_PLAYER,
4571     SCREEN_MASK_INPUT,
4572     "next player"
4573   },
4574 };
4575
4576 static struct
4577 {
4578   int gfx_unpressed, gfx_pressed;
4579   int x, y;
4580   int gadget_id;
4581   char *infotext;
4582 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
4583 {
4584   {
4585     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
4586     SC_SCROLL_UP_XPOS, SC_SCROLL_UP_YPOS,
4587     SCREEN_CTRL_ID_SCROLL_UP,
4588     "scroll up"
4589   },
4590   {
4591     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
4592     SC_SCROLL_DOWN_XPOS, SC_SCROLL_DOWN_YPOS,
4593     SCREEN_CTRL_ID_SCROLL_DOWN,
4594     "scroll down"
4595   }
4596 };
4597
4598 static struct
4599 {
4600 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4601   Bitmap **gfx_unpressed, **gfx_pressed;
4602 #else
4603   int gfx_unpressed, gfx_pressed;
4604 #endif
4605   int x, y;
4606   int width, height;
4607   int type;
4608   int gadget_id;
4609   char *infotext;
4610 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
4611 {
4612   {
4613 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4614     &scrollbar_bitmap[0], &scrollbar_bitmap[1],
4615 #else
4616     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
4617 #endif
4618     SC_SCROLL_VERTICAL_XPOS, SC_SCROLL_VERTICAL_YPOS,
4619     SC_SCROLL_VERTICAL_XSIZE, SC_SCROLL_VERTICAL_YSIZE,
4620     GD_TYPE_SCROLLBAR_VERTICAL,
4621     SCREEN_CTRL_ID_SCROLL_VERTICAL,
4622     "scroll level series vertically"
4623   }
4624 };
4625
4626 static void CreateScreenMenubuttons()
4627 {
4628   struct GadgetInfo *gi;
4629   unsigned long event_mask;
4630   int i;
4631
4632   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
4633   {
4634     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4635     int gfx_unpressed, gfx_pressed;
4636     int x, y, width, height;
4637     int gd_x1, gd_x2, gd_y1, gd_y2;
4638     int id = menubutton_info[i].gadget_id;
4639
4640     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
4641
4642     menubutton_info[i].get_gadget_position(&x, &y, id);
4643
4644     width = SC_MENUBUTTON_XSIZE;
4645     height = SC_MENUBUTTON_YSIZE;
4646
4647     gfx_unpressed = menubutton_info[i].gfx_unpressed;
4648     gfx_pressed   = menubutton_info[i].gfx_pressed;
4649     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4650     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4651     gd_x1 = graphic_info[gfx_unpressed].src_x;
4652     gd_y1 = graphic_info[gfx_unpressed].src_y;
4653     gd_x2 = graphic_info[gfx_pressed].src_x;
4654     gd_y2 = graphic_info[gfx_pressed].src_y;
4655
4656     gi = CreateGadget(GDI_CUSTOM_ID, id,
4657                       GDI_CUSTOM_TYPE_ID, i,
4658                       GDI_INFO_TEXT, menubutton_info[i].infotext,
4659                       GDI_X, x,
4660                       GDI_Y, y,
4661                       GDI_WIDTH, width,
4662                       GDI_HEIGHT, height,
4663                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4664                       GDI_STATE, GD_BUTTON_UNPRESSED,
4665                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4666                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4667                       GDI_DIRECT_DRAW, FALSE,
4668                       GDI_EVENT_MASK, event_mask,
4669                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4670                       GDI_END);
4671
4672     if (gi == NULL)
4673       Error(ERR_EXIT, "cannot create gadget");
4674
4675     screen_gadget[id] = gi;
4676   }
4677 }
4678
4679 static void CreateScreenScrollbuttons()
4680 {
4681   struct GadgetInfo *gi;
4682   unsigned long event_mask;
4683   int i;
4684
4685   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
4686   {
4687     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4688     int gfx_unpressed, gfx_pressed;
4689     int x, y, width, height;
4690     int gd_x1, gd_x2, gd_y1, gd_y2;
4691     int id = scrollbutton_info[i].gadget_id;
4692
4693     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
4694
4695     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
4696     y = mSY + scrollbutton_info[i].y;
4697     width = SC_SCROLLBUTTON_XSIZE;
4698     height = SC_SCROLLBUTTON_YSIZE;
4699
4700     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
4701       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
4702                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
4703
4704     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
4705     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
4706     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4707     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4708     gd_x1 = graphic_info[gfx_unpressed].src_x;
4709     gd_y1 = graphic_info[gfx_unpressed].src_y;
4710     gd_x2 = graphic_info[gfx_pressed].src_x;
4711     gd_y2 = graphic_info[gfx_pressed].src_y;
4712
4713     gi = CreateGadget(GDI_CUSTOM_ID, id,
4714                       GDI_CUSTOM_TYPE_ID, i,
4715                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
4716                       GDI_X, x,
4717                       GDI_Y, y,
4718                       GDI_WIDTH, width,
4719                       GDI_HEIGHT, height,
4720                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4721                       GDI_STATE, GD_BUTTON_UNPRESSED,
4722                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4723                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4724                       GDI_DIRECT_DRAW, FALSE,
4725                       GDI_EVENT_MASK, event_mask,
4726                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4727                       GDI_END);
4728
4729     if (gi == NULL)
4730       Error(ERR_EXIT, "cannot create gadget");
4731
4732     screen_gadget[id] = gi;
4733   }
4734 }
4735
4736 static void CreateScreenScrollbars()
4737 {
4738   int i;
4739
4740   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
4741   {
4742     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4743 #if !defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4744     int gfx_unpressed, gfx_pressed;
4745 #endif
4746     int x, y, width, height;
4747     int gd_x1, gd_x2, gd_y1, gd_y2;
4748     struct GadgetInfo *gi;
4749     int items_max, items_visible, item_position;
4750     unsigned long event_mask;
4751     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
4752     int id = scrollbar_info[i].gadget_id;
4753
4754     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
4755
4756     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
4757     y = mSY + scrollbar_info[i].y;
4758     width  = scrollbar_info[i].width;
4759     height = scrollbar_info[i].height;
4760
4761     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
4762       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
4763
4764     items_max = num_page_entries;
4765     items_visible = num_page_entries;
4766     item_position = 0;
4767
4768 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4769     gd_bitmap_unpressed = *scrollbar_info[i].gfx_unpressed;
4770     gd_bitmap_pressed   = *scrollbar_info[i].gfx_pressed;
4771     gd_x1 = 0;
4772     gd_y1 = 0;
4773     gd_x2 = 0;
4774     gd_y2 = 0;
4775 #else
4776     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
4777     gfx_pressed   = scrollbar_info[i].gfx_pressed;
4778     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4779     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4780     gd_x1 = graphic_info[gfx_unpressed].src_x;
4781     gd_y1 = graphic_info[gfx_unpressed].src_y;
4782     gd_x2 = graphic_info[gfx_pressed].src_x;
4783     gd_y2 = graphic_info[gfx_pressed].src_y;
4784 #endif
4785
4786     gi = CreateGadget(GDI_CUSTOM_ID, id,
4787                       GDI_CUSTOM_TYPE_ID, i,
4788                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
4789                       GDI_X, x,
4790                       GDI_Y, y,
4791                       GDI_WIDTH, width,
4792                       GDI_HEIGHT, height,
4793                       GDI_TYPE, scrollbar_info[i].type,
4794                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
4795                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
4796                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
4797 #if 1
4798                       GDI_WHEEL_AREA_X, SX,
4799                       GDI_WHEEL_AREA_Y, SY,
4800                       GDI_WHEEL_AREA_WIDTH, SXSIZE,
4801                       GDI_WHEEL_AREA_HEIGHT, SYSIZE,
4802 #else
4803                       GDI_WHEEL_AREA_X, 0,
4804                       GDI_WHEEL_AREA_Y, 0,
4805                       GDI_WHEEL_AREA_WIDTH, WIN_XSIZE,
4806                       GDI_WHEEL_AREA_HEIGHT, WIN_YSIZE,
4807 #endif
4808                       GDI_STATE, GD_BUTTON_UNPRESSED,
4809                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4810                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4811                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
4812                       GDI_DIRECT_DRAW, FALSE,
4813                       GDI_EVENT_MASK, event_mask,
4814                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4815                       GDI_END);
4816
4817     if (gi == NULL)
4818       Error(ERR_EXIT, "cannot create gadget");
4819
4820     screen_gadget[id] = gi;
4821   }
4822 }
4823
4824 void CreateScreenGadgets()
4825 {
4826   int last_game_status = game_status;   /* save current game status */
4827
4828 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4829   int i;
4830
4831   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
4832   {
4833     scrollbar_bitmap[i] = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
4834
4835     /* copy pointers to clip mask and GC */
4836     scrollbar_bitmap[i]->clip_mask =
4837       graphic_info[IMG_MENU_SCROLLBAR + i].clip_mask;
4838     scrollbar_bitmap[i]->stored_clip_gc =
4839       graphic_info[IMG_MENU_SCROLLBAR + i].clip_gc;
4840
4841     BlitBitmap(graphic_info[IMG_MENU_SCROLLBAR + i].bitmap,
4842                scrollbar_bitmap[i],
4843                graphic_info[IMG_MENU_SCROLLBAR + i].src_x,
4844                graphic_info[IMG_MENU_SCROLLBAR + i].src_y,
4845                TILEX, TILEY, 0, 0);
4846   }
4847 #endif
4848
4849   CreateScreenMenubuttons();
4850
4851   /* force LEVELS draw offset for scrollbar / scrollbutton gadgets */
4852   game_status = GAME_MODE_LEVELS;
4853
4854   CreateScreenScrollbuttons();
4855   CreateScreenScrollbars();
4856
4857   game_status = last_game_status;       /* restore current game status */
4858 }
4859
4860 void FreeScreenGadgets()
4861 {
4862   int i;
4863
4864 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4865   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
4866   {
4867     /* prevent freeing clip mask and GC twice */
4868     scrollbar_bitmap[i]->clip_mask = None;
4869     scrollbar_bitmap[i]->stored_clip_gc = None;
4870
4871     FreeBitmap(scrollbar_bitmap[i]);
4872   }
4873 #endif
4874
4875   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
4876     FreeGadget(screen_gadget[i]);
4877 }
4878
4879 void MapScreenMenuGadgets(int screen_mask)
4880 {
4881   int i;
4882
4883   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
4884     if (screen_mask & menubutton_info[i].screen_mask)
4885       MapGadget(screen_gadget[menubutton_info[i].gadget_id]);
4886 }
4887
4888 void MapScreenTreeGadgets(TreeInfo *ti)
4889 {
4890   int num_entries = numTreeInfoInGroup(ti);
4891   int i;
4892
4893   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
4894     return;
4895
4896   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
4897     MapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
4898
4899   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
4900     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
4901 }
4902
4903 static void HandleScreenGadgets(struct GadgetInfo *gi)
4904 {
4905   int id = gi->custom_id;
4906   int button = gi->event.button;
4907   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4908
4909   switch (id)
4910   {
4911     case SCREEN_CTRL_ID_PREV_LEVEL:
4912       HandleMainMenu_SelectLevel(step, -1);
4913       break;
4914
4915     case SCREEN_CTRL_ID_NEXT_LEVEL:
4916       HandleMainMenu_SelectLevel(step, +1);
4917       break;
4918
4919     case SCREEN_CTRL_ID_PREV_PLAYER:
4920       HandleSetupScreen_Input_Player(step, -1);
4921       break;
4922
4923     case SCREEN_CTRL_ID_NEXT_PLAYER:
4924       HandleSetupScreen_Input_Player(step, +1);
4925       break;
4926
4927     case SCREEN_CTRL_ID_SCROLL_UP:
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_DOWN:
4935       if (game_status == GAME_MODE_LEVELS)
4936         HandleChooseLevel(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4937       else if (game_status == GAME_MODE_SETUP)
4938         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4939       break;
4940
4941     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
4942       if (game_status == GAME_MODE_LEVELS)
4943         HandleChooseLevel(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4944       else if (game_status == GAME_MODE_SETUP)
4945         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4946       break;
4947
4948     default:
4949       break;
4950   }
4951 }