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