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