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