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