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