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