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