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