rnd-20060610-1-src
[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   { TYPE_SWITCH,        &setup.editor.el_by_game,   "Show elements by game:" },
2583   { TYPE_SWITCH,        &setup.editor.el_by_type,   "Show elements by type:" },
2584   { TYPE_EMPTY,         NULL,                   ""                      },
2585   { TYPE_SWITCH, &setup.editor.show_element_token,      "Show element token:" },
2586   { TYPE_EMPTY,         NULL,                   ""                      },
2587   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2588
2589   { 0,                  NULL,                   NULL                    }
2590 };
2591
2592 static struct TokenInfo setup_info_graphics[] =
2593 {
2594   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen Mode:"      },
2595   { TYPE_SWITCH,        &setup.scroll_delay,    "Delayed Scrolling:"    },
2596 #if 0
2597   { TYPE_SWITCH,        &setup.soft_scrolling,  "Soft Scrolling:"       },
2598   { TYPE_SWITCH,        &setup.double_buffering,"Double-Buffering:"     },
2599   { TYPE_SWITCH,        &setup.fading,          "Fading:"               },
2600 #endif
2601   { TYPE_SWITCH,        &setup.quick_switch,    "Quick Player Focus Switch:" },
2602   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Menu Doors:"     },
2603   { TYPE_SWITCH,        &setup.show_titlescreen,"Show Title Screens:"   },
2604   { TYPE_SWITCH,        &setup.toons,           "Show Toons:"           },
2605   { TYPE_ECS_AGA,       &setup.prefer_aga_graphics,"EMC graphics preference:" },
2606   { TYPE_EMPTY,         NULL,                   ""                      },
2607   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2608
2609   { 0,                  NULL,                   NULL                    }
2610 };
2611
2612 static struct TokenInfo setup_info_sound[] =
2613 {
2614   { TYPE_SWITCH,        &setup.sound_simple,    "Sound Effects (Normal):"  },
2615   { TYPE_SWITCH,        &setup.sound_loops,     "Sound Effects (Looping):" },
2616   { TYPE_SWITCH,        &setup.sound_music,     "Music:"                },
2617   { TYPE_EMPTY,         NULL,                   ""                      },
2618   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2619
2620   { 0,                  NULL,                   NULL                    }
2621 };
2622
2623 static struct TokenInfo setup_info_artwork[] =
2624 {
2625   { TYPE_ENTER_MENU,    execSetupChooseGraphics,"Custom Graphics"       },
2626   { TYPE_STRING,        &graphics_set_name,     ""                      },
2627   { TYPE_ENTER_MENU,    execSetupChooseSounds,  "Custom Sounds"         },
2628   { TYPE_STRING,        &sounds_set_name,       ""                      },
2629   { TYPE_ENTER_MENU,    execSetupChooseMusic,   "Custom Music"          },
2630   { TYPE_STRING,        &music_set_name,        ""                      },
2631   { TYPE_EMPTY,         NULL,                   ""                      },
2632 #if 1
2633   { TYPE_YES_NO, &setup.override_level_graphics,"Override Level Graphics:" },
2634   { TYPE_YES_NO, &setup.override_level_sounds,  "Override Level Sounds:"   },
2635   { TYPE_YES_NO, &setup.override_level_music,   "Override Level Music:"    },
2636 #else
2637   { TYPE_STRING,        NULL,                   "Override Level Artwork:"},
2638   { TYPE_YES_NO,        &setup.override_level_graphics, "Graphics:"     },
2639   { TYPE_YES_NO,        &setup.override_level_sounds,   "Sounds:"       },
2640   { TYPE_YES_NO,        &setup.override_level_music,    "Music:"        },
2641 #endif
2642   { TYPE_EMPTY,         NULL,                   ""                      },
2643   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2644
2645   { 0,                  NULL,                   NULL                    }
2646 };
2647
2648 static struct TokenInfo setup_info_shortcut_1[] =
2649 {
2650   { TYPE_KEYTEXT,       NULL,           "Quick Save Game to Tape:",     },
2651   { TYPE_KEY,           &setup.shortcut.save_game, ""                   },
2652   { TYPE_KEYTEXT,       NULL,           "Quick Load Game from Tape:",   },
2653   { TYPE_KEY,           &setup.shortcut.load_game, ""                   },
2654   { TYPE_KEYTEXT,       NULL,           "Start Game & Toggle Pause:",   },
2655   { TYPE_KEY,           &setup.shortcut.toggle_pause, ""                },
2656   { TYPE_EMPTY,         NULL,                   ""                      },
2657   { TYPE_YES_NO,        &setup.ask_on_escape,   "Ask on 'Esc' Key:"     },
2658   { TYPE_YES_NO, &setup.ask_on_escape_editor,   "Ask on 'Esc' Key (Editor):" },
2659   { TYPE_EMPTY,         NULL,                   ""                      },
2660   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2661
2662   { 0,                  NULL,                   NULL                    }
2663 };
2664
2665 static struct TokenInfo setup_info_shortcut_2[] =
2666 {
2667   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 1:",       },
2668   { TYPE_KEY,           &setup.shortcut.focus_player[0], ""             },
2669   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 2:",       },
2670   { TYPE_KEY,           &setup.shortcut.focus_player[1], ""             },
2671   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 3:",       },
2672   { TYPE_KEY,           &setup.shortcut.focus_player[2], ""             },
2673   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 4:",       },
2674   { TYPE_KEY,           &setup.shortcut.focus_player[3], ""             },
2675   { TYPE_KEYTEXT,       NULL,           "Set Focus to All Players:",    },
2676   { TYPE_KEY,           &setup.shortcut.focus_player_all, ""            },
2677   { TYPE_EMPTY,         NULL,                   ""                      },
2678   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2679
2680   { 0,                  NULL,                   NULL                    }
2681 };
2682
2683 static Key getSetupKey()
2684 {
2685   Key key = KSYM_UNDEFINED;
2686   boolean got_key_event = FALSE;
2687
2688   while (!got_key_event)
2689   {
2690     if (PendingEvent())         /* got event */
2691     {
2692       Event event;
2693
2694       NextEvent(&event);
2695
2696       switch(event.type)
2697       {
2698         case EVENT_KEYPRESS:
2699           {
2700             key = GetEventKey((KeyEvent *)&event, TRUE);
2701
2702             /* press 'Escape' or 'Enter' to keep the existing key binding */
2703             if (key == KSYM_Escape || key == KSYM_Return)
2704               key = KSYM_UNDEFINED;     /* keep old value */
2705
2706             got_key_event = TRUE;
2707           }
2708           break;
2709
2710         case EVENT_KEYRELEASE:
2711           key_joystick_mapping = 0;
2712           break;
2713
2714         default:
2715           HandleOtherEvents(&event);
2716           break;
2717       }
2718     }
2719
2720     DoAnimation();
2721     BackToFront();
2722
2723     /* don't eat all CPU time */
2724     Delay(10);
2725   }
2726
2727   return key;
2728 }
2729
2730 static int getSetupTextFont(int type)
2731 {
2732   if (type & (TYPE_SWITCH |
2733               TYPE_YES_NO |
2734               TYPE_STRING |
2735               TYPE_ECS_AGA |
2736               TYPE_KEYTEXT))
2737     return FONT_MENU_2;
2738   else
2739     return FONT_MENU_1;
2740 }
2741
2742 static int getSetupValueFont(int type, void *value)
2743 {
2744   if (type & TYPE_KEY)
2745     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
2746   else if (type & TYPE_STRING)
2747     return FONT_VALUE_2;
2748   else if (type & TYPE_ECS_AGA)
2749     return FONT_VALUE_1;
2750   else if (type & TYPE_BOOLEAN_STYLE)
2751     return (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
2752   else
2753     return FONT_VALUE_1;
2754 }
2755
2756 static void drawSetupValue(int pos)
2757 {
2758   boolean font_draw_xoffset_modified = FALSE;
2759   int font_draw_xoffset_old = -1;
2760   int xpos = MENU_SCREEN_VALUE_XPOS;
2761   int ypos = MENU_SCREEN_START_YPOS + pos;
2762   int startx = mSX + xpos * 32;
2763   int starty = mSY + ypos * 32;
2764   int font_nr, font_width;
2765   int type = setup_info[pos].type;
2766   void *value = setup_info[pos].value;
2767   char *value_string = getSetupValue(type, value);
2768   int i;
2769
2770   if (value_string == NULL)
2771     return;
2772
2773   if (type & TYPE_KEY)
2774   {
2775 #if 1
2776     xpos = MENU_SCREEN_START_XPOS;
2777 #else
2778     xpos = 3;
2779 #endif
2780
2781     if (type & TYPE_QUERY)
2782     {
2783       value_string = "<press key>";
2784 #if 0
2785       font_nr = FONT_INPUT_1_ACTIVE;
2786 #endif
2787     }
2788   }
2789   else if (type & TYPE_STRING)
2790   {
2791     int max_value_len = (SCR_FIELDX - 2) * 2;
2792
2793     xpos = MENU_SCREEN_START_XPOS;
2794 #if 0
2795     font_nr = FONT_VALUE_2;
2796 #endif
2797
2798     if (strlen(value_string) > max_value_len)
2799       value_string[max_value_len] = '\0';
2800   }
2801   else if (type & TYPE_ECS_AGA)
2802   {
2803 #if 0
2804     font_nr = FONT_VALUE_1;
2805 #endif
2806   }
2807   else if (type & TYPE_BOOLEAN_STYLE)
2808   {
2809 #if 0
2810     font_nr = (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
2811 #endif
2812   }
2813
2814   startx = mSX + xpos * 32;
2815   starty = mSY + ypos * 32;
2816   font_nr = getSetupValueFont(type, value);
2817   font_width = getFontWidth(font_nr);
2818
2819   /* downward compatibility correction for Juergen Bonhagen's menu settings */
2820   if (setup_mode != SETUP_MODE_INPUT)
2821   {
2822     int check_font_nr = FONT_OPTION_ON; /* known font that needs correction */
2823     int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
2824     int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset;
2825     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
2826     int text_font_nr = getSetupTextFont(FONT_MENU_2);
2827     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
2828     int text_width = MAX_MENU_TEXT_LENGTH_MEDIUM * getFontWidth(text_font_nr);
2829     boolean correct_font_draw_xoffset = FALSE;
2830
2831     if (xpos == MENU_SCREEN_START_XPOS &&
2832         startx + font1_xoffset < text_startx + text_font_xoffset)
2833       correct_font_draw_xoffset = TRUE;
2834
2835     if (xpos == MENU_SCREEN_VALUE_XPOS &&
2836         startx + font2_xoffset < text_startx + text_width + text_font_xoffset)
2837       correct_font_draw_xoffset = TRUE;
2838
2839 #if 0
2840     printf("::: %d + %d < %d + %d + %d\n",
2841            startx, font_xoffset, text_startx, text_width, text_font_xoffset);
2842     printf("::: => need correction == %d\n", correct_font_draw_xoffset);
2843 #endif
2844
2845     /* check if setup value would overlap with setup text when printed */
2846     /* (this can happen for extreme/wrong values for font draw offset) */
2847     if (correct_font_draw_xoffset)
2848     {
2849       font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset;
2850       font_draw_xoffset_modified = TRUE;
2851
2852       if (type & TYPE_KEY)
2853         getFontBitmapInfo(font_nr)->draw_xoffset += 2 * getFontWidth(font_nr);
2854       else if (!(type & TYPE_STRING))
2855         getFontBitmapInfo(font_nr)->draw_xoffset = text_font_xoffset + 20 -
2856           MAX_MENU_TEXT_LENGTH_MEDIUM * (16 - getFontWidth(text_font_nr));
2857     }
2858   }
2859
2860 #if 1
2861   for (i = 0; i <= MENU_SCREEN_MAX_XPOS - xpos; i++)
2862     DrawText(startx + i * font_width, starty, " ", font_nr);
2863 #else
2864 #if 1
2865   for (i = xpos; i <= MENU_SCREEN_MAX_XPOS; i++)
2866     DrawText(mSX + i * 32, starty, " ", font_nr);
2867 #else
2868   DrawText(startx, starty,
2869            (xpos == 3 ? "              " : "   "), font_nr);
2870 #endif
2871 #endif
2872
2873   DrawText(startx, starty, value_string, font_nr);
2874
2875   if (font_draw_xoffset_modified)
2876     getFontBitmapInfo(font_nr)->draw_xoffset = font_draw_xoffset_old;
2877 }
2878
2879 static void changeSetupValue(int pos)
2880 {
2881   if (setup_info[pos].type & TYPE_BOOLEAN_STYLE)
2882   {
2883     *(boolean *)setup_info[pos].value ^= TRUE;
2884   }
2885   else if (setup_info[pos].type & TYPE_KEY)
2886   {
2887     Key key;
2888
2889     setup_info[pos].type |= TYPE_QUERY;
2890     drawSetupValue(pos);
2891     setup_info[pos].type &= ~TYPE_QUERY;
2892
2893     key = getSetupKey();
2894     if (key != KSYM_UNDEFINED)
2895       *(Key *)setup_info[pos].value = key;
2896   }
2897
2898   drawSetupValue(pos);
2899 }
2900
2901 static void DrawSetupScreen_Generic()
2902 {
2903   char *title_string = NULL;
2904   int i;
2905
2906   UnmapAllGadgets();
2907   CloseDoor(DOOR_CLOSE_2);
2908
2909   ClearWindow();
2910
2911   if (setup_mode == SETUP_MODE_MAIN)
2912   {
2913     setup_info = setup_info_main;
2914     title_string = "Setup";
2915   }
2916   else if (setup_mode == SETUP_MODE_GAME)
2917   {
2918     setup_info = setup_info_game;
2919     title_string = "Setup Game";
2920   }
2921   else if (setup_mode == SETUP_MODE_EDITOR)
2922   {
2923     setup_info = setup_info_editor;
2924     title_string = "Setup Editor";
2925   }
2926   else if (setup_mode == SETUP_MODE_GRAPHICS)
2927   {
2928     setup_info = setup_info_graphics;
2929     title_string = "Setup Graphics";
2930   }
2931   else if (setup_mode == SETUP_MODE_SOUND)
2932   {
2933     setup_info = setup_info_sound;
2934     title_string = "Setup Sound";
2935   }
2936   else if (setup_mode == SETUP_MODE_ARTWORK)
2937   {
2938     setup_info = setup_info_artwork;
2939     title_string = "Custom Artwork";
2940   }
2941   else if (setup_mode == SETUP_MODE_SHORTCUT_1)
2942   {
2943     setup_info = setup_info_shortcut_1;
2944     title_string = "Setup Shortcuts";
2945   }
2946   else if (setup_mode == SETUP_MODE_SHORTCUT_2)
2947   {
2948     setup_info = setup_info_shortcut_2;
2949     title_string = "Setup Shortcuts";
2950   }
2951
2952 #if 1
2953   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
2954 #else
2955   DrawText(mSX + 16, mSY + 16, title_string, FONT_TITLE_1);
2956 #endif
2957
2958   num_setup_info = 0;
2959   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
2960   {
2961     void *value_ptr = setup_info[i].value;
2962     int xpos = MENU_SCREEN_START_XPOS;
2963     int ypos = MENU_SCREEN_START_YPOS + i;
2964 #if 1
2965     int font_nr;
2966 #else
2967     int font_nr = FONT_MENU_1;
2968 #endif
2969
2970     /* set some entries to "unchangeable" according to other variables */
2971     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
2972         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
2973         (value_ptr == &setup.sound_music  && !audio.music_available) ||
2974         (value_ptr == &setup.fullscreen   && !video.fullscreen_available))
2975       setup_info[i].type |= TYPE_GHOSTED;
2976
2977 #if 1
2978     font_nr = getSetupTextFont(setup_info[i].type);
2979 #else
2980 #if 1
2981     if (setup_info[i].type & (TYPE_SWITCH |
2982                               TYPE_YES_NO |
2983                               TYPE_STRING |
2984                               TYPE_ECS_AGA |
2985                               TYPE_KEYTEXT))
2986       font_nr = FONT_MENU_2;
2987 #else
2988     if (setup_info[i].type & TYPE_STRING)
2989       font_nr = FONT_MENU_2;
2990 #endif
2991 #endif
2992
2993     DrawText(mSX + xpos * 32, mSY + ypos * 32, setup_info[i].text, font_nr);
2994
2995     if (setup_info[i].type & TYPE_ENTER_MENU)
2996       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
2997     else if (setup_info[i].type & TYPE_LEAVE_MENU)
2998       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
2999     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
3000       initCursor(i, IMG_MENU_BUTTON);
3001
3002     if (setup_info[i].type & TYPE_VALUE)
3003       drawSetupValue(i);
3004
3005     num_setup_info++;
3006   }
3007
3008 #if 0
3009   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3010                     "Joysticks deactivated in setup menu");
3011 #endif
3012
3013   FadeToFront();
3014   InitAnimation();
3015   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
3016 }
3017
3018 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
3019 {
3020   static int choice_store[MAX_SETUP_MODES];
3021   int choice = choice_store[setup_mode];        /* always starts with 0 */
3022   int x = 0;
3023   int y = choice;
3024
3025   if (button == MB_MENU_INITIALIZE)
3026   {
3027     /* advance to first valid menu entry */
3028     while (choice < num_setup_info &&
3029            setup_info[choice].type & TYPE_SKIP_ENTRY)
3030       choice++;
3031     choice_store[setup_mode] = choice;
3032
3033     drawCursor(choice, FC_RED);
3034     return;
3035   }
3036   else if (button == MB_MENU_LEAVE)
3037   {
3038     for (y = 0; y < num_setup_info; y++)
3039     {
3040       if (setup_info[y].type & TYPE_LEAVE_MENU)
3041       {
3042         void (*menu_callback_function)(void) = setup_info[y].value;
3043
3044         menu_callback_function();
3045         break;  /* absolutely needed because function changes 'setup_info'! */
3046       }
3047     }
3048
3049     return;
3050   }
3051
3052   if (mx || my)         /* mouse input */
3053   {
3054     x = (mx - mSX) / 32;
3055     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3056   }
3057   else if (dx || dy)    /* keyboard input */
3058   {
3059     if (dx)
3060     {
3061       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE_MENU : TYPE_ENTER_MENU);
3062
3063       if (setup_info[choice].type & menu_navigation_type ||
3064           setup_info[choice].type & TYPE_BOOLEAN_STYLE)
3065         button = MB_MENU_CHOICE;
3066     }
3067     else if (dy)
3068       y = choice + dy;
3069
3070     /* jump to next non-empty menu entry (up or down) */
3071     while (y > 0 && y < num_setup_info - 1 &&
3072            setup_info[y].type & TYPE_SKIP_ENTRY)
3073       y += dy;
3074   }
3075
3076   if (IN_VIS_FIELD(x, y) &&
3077       y >= 0 && y < num_setup_info && setup_info[y].type & ~TYPE_SKIP_ENTRY)
3078   {
3079     if (button)
3080     {
3081       if (y != choice)
3082       {
3083         drawCursor(y, FC_RED);
3084         drawCursor(choice, FC_BLUE);
3085         choice = choice_store[setup_mode] = y;
3086       }
3087     }
3088     else if (!(setup_info[y].type & TYPE_GHOSTED))
3089     {
3090       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE_MENU)
3091       {
3092         void (*menu_callback_function)(void) = setup_info[choice].value;
3093
3094         menu_callback_function();
3095       }
3096       else
3097       {
3098         if (setup_info[y].type & TYPE_KEYTEXT &&
3099             setup_info[y + 1].type & TYPE_KEY)
3100           y++;
3101
3102         if (setup_info[y].type & TYPE_VALUE)
3103           changeSetupValue(y);
3104       }
3105     }
3106   }
3107 }
3108
3109 void DrawSetupScreen_Input()
3110 {
3111   ClearWindow();
3112
3113 #if 1
3114   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Setup Input");
3115 #else
3116   DrawText(mSX + 16, mSY + 16, "Setup Input", FONT_TITLE_1);
3117 #endif
3118
3119   initCursor(0,  IMG_MENU_BUTTON);
3120   initCursor(1,  IMG_MENU_BUTTON);
3121   initCursor(2,  IMG_MENU_BUTTON_ENTER_MENU);
3122   initCursor(13, IMG_MENU_BUTTON_LEAVE_MENU);
3123
3124   drawCursorXY(10, 0, IMG_MENU_BUTTON_LEFT);
3125   drawCursorXY(12, 0, IMG_MENU_BUTTON_RIGHT);
3126
3127   DrawText(mSX + 32, mSY +  2 * 32, "Player:", FONT_MENU_1);
3128   DrawText(mSX + 32, mSY +  3 * 32, "Device:", FONT_MENU_1);
3129   DrawText(mSX + 32, mSY + 15 * 32, "Back",   FONT_MENU_1);
3130
3131 #if 0
3132   DeactivateJoystickForCalibration();
3133 #endif
3134 #if 1
3135   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3136                     "Joysticks deactivated on this screen");
3137 #endif
3138
3139   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
3140   FadeToFront();
3141   InitAnimation();
3142 }
3143
3144 static void setJoystickDeviceToNr(char *device_name, int device_nr)
3145 {
3146   if (device_name == NULL)
3147     return;
3148
3149   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
3150     device_nr = 0;
3151
3152   if (strlen(device_name) > 1)
3153   {
3154     char c1 = device_name[strlen(device_name) - 1];
3155     char c2 = device_name[strlen(device_name) - 2];
3156
3157     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
3158       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
3159   }
3160   else
3161     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
3162             strlen(device_name));
3163 }
3164
3165 static void drawPlayerSetupInputInfo(int player_nr)
3166 {
3167   int i;
3168   static struct SetupKeyboardInfo custom_key;
3169   static struct
3170   {
3171     Key *key;
3172     char *text;
3173   } custom[] =
3174   {
3175     { &custom_key.left,  "Joystick Left"  },
3176     { &custom_key.right, "Joystick Right" },
3177     { &custom_key.up,    "Joystick Up"    },
3178     { &custom_key.down,  "Joystick Down"  },
3179     { &custom_key.snap,  "Button 1"       },
3180     { &custom_key.drop,  "Button 2"       }
3181   };
3182   static char *joystick_name[MAX_PLAYERS] =
3183   {
3184     "Joystick1",
3185     "Joystick2",
3186     "Joystick3",
3187     "Joystick4"
3188   };
3189
3190   InitJoysticks();
3191
3192   custom_key = setup.input[player_nr].key;
3193
3194   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
3195            FONT_INPUT_1_ACTIVE);
3196
3197   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3198                              TILEX, TILEY);
3199   DrawGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3200                          PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
3201
3202   if (setup.input[player_nr].use_joystick)
3203   {
3204     char *device_name = setup.input[player_nr].joy.device_name;
3205     char *text = joystick_name[getJoystickNrFromDeviceName(device_name)];
3206     int font_nr = (joystick.fd[player_nr] < 0 ? FONT_VALUE_OLD : FONT_VALUE_1);
3207
3208     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
3209     DrawText(mSX + 32, mSY + 4 * 32, "Calibrate", FONT_MENU_1);
3210   }
3211   else
3212   {
3213     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", FONT_VALUE_1);
3214     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", FONT_MENU_1);
3215   }
3216
3217   DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", FONT_MENU_1);
3218   drawCursorXY(1, 4, IMG_MENU_BUTTON_LEFT);
3219   drawCursorXY(1, 5, IMG_MENU_BUTTON_RIGHT);
3220   drawCursorXY(1, 6, IMG_MENU_BUTTON_UP);
3221   drawCursorXY(1, 7, IMG_MENU_BUTTON_DOWN);
3222   DrawText(mSX + 2 * 32, mSY +  6 * 32, ":", FONT_VALUE_OLD);
3223   DrawText(mSX + 2 * 32, mSY +  7 * 32, ":", FONT_VALUE_OLD);
3224   DrawText(mSX + 2 * 32, mSY +  8 * 32, ":", FONT_VALUE_OLD);
3225   DrawText(mSX + 2 * 32, mSY +  9 * 32, ":", FONT_VALUE_OLD);
3226   DrawText(mSX + 1 * 32, mSY + 10 * 32, "Snap Field:", FONT_VALUE_OLD);
3227   DrawText(mSX + 1 * 32, mSY + 12 * 32, "Drop Element:", FONT_VALUE_OLD);
3228
3229   for (i = 0; i < 6; i++)
3230   {
3231     int ypos = 6 + i + (i > 3 ? i-3 : 0);
3232
3233     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3234              "              ", FONT_VALUE_1);
3235     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3236              (setup.input[player_nr].use_joystick ?
3237               custom[i].text :
3238               getKeyNameFromKey(*custom[i].key)), FONT_VALUE_1);
3239   }
3240 }
3241
3242 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
3243 {
3244   static int choice = 0;
3245   static int player_nr = 0;
3246   int x = 0;
3247   int y = choice;
3248   int pos_start  = SETUPINPUT_SCREEN_POS_START;
3249   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
3250   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
3251   int pos_end    = SETUPINPUT_SCREEN_POS_END;
3252
3253   if (button == MB_MENU_INITIALIZE)
3254   {
3255     drawPlayerSetupInputInfo(player_nr);
3256     drawCursor(choice, FC_RED);
3257
3258     return;
3259   }
3260   else if (button == MB_MENU_LEAVE)
3261   {
3262     setup_mode = SETUP_MODE_MAIN;
3263     DrawSetupScreen();
3264     InitJoysticks();
3265
3266     return;
3267   }
3268
3269   if (mx || my)         /* mouse input */
3270   {
3271     x = (mx - mSX) / 32;
3272     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3273   }
3274   else if (dx || dy)    /* keyboard input */
3275   {
3276     if (dx && choice == 0)
3277       x = (dx < 0 ? 10 : 12);
3278     else if ((dx && choice == 1) ||
3279              (dx == +1 && choice == 2) ||
3280              (dx == -1 && choice == pos_end))
3281       button = MB_MENU_CHOICE;
3282     else if (dy)
3283       y = choice + dy;
3284
3285     if (y >= pos_empty1 && y <= pos_empty2)
3286       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
3287   }
3288
3289   if (IN_VIS_FIELD(x, y) &&
3290       y == 0 && ((x < 10 && !button) || ((x == 10 || x == 12) && button)))
3291   {
3292     static unsigned long delay = 0;
3293
3294     if (!DelayReached(&delay, GADGET_FRAME_DELAY))
3295       return;
3296
3297     player_nr = (player_nr + (x == 10 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
3298
3299     drawPlayerSetupInputInfo(player_nr);
3300   }
3301   else if (IN_VIS_FIELD(x, y) &&
3302            y >= pos_start && y <= pos_end &&
3303            !(y >= pos_empty1 && y <= pos_empty2))
3304   {
3305     if (button)
3306     {
3307       if (y != choice)
3308       {
3309         drawCursor(y, FC_RED);
3310         drawCursor(choice, FC_BLUE);
3311         choice = y;
3312       }
3313     }
3314     else
3315     {
3316       if (y == 1)
3317       {
3318         char *device_name = setup.input[player_nr].joy.device_name;
3319
3320         if (!setup.input[player_nr].use_joystick)
3321         {
3322           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
3323
3324           setJoystickDeviceToNr(device_name, new_device_nr);
3325           setup.input[player_nr].use_joystick = TRUE;
3326         }
3327         else
3328         {
3329           int device_nr = getJoystickNrFromDeviceName(device_name);
3330           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
3331
3332           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
3333             setup.input[player_nr].use_joystick = FALSE;
3334           else
3335             setJoystickDeviceToNr(device_name, new_device_nr);
3336         }
3337
3338         drawPlayerSetupInputInfo(player_nr);
3339       }
3340       else if (y == 2)
3341       {
3342         if (setup.input[player_nr].use_joystick)
3343         {
3344           InitJoysticks();
3345           CalibrateJoystick(player_nr);
3346         }
3347         else
3348           CustomizeKeyboard(player_nr);
3349       }
3350       else if (y == pos_end)
3351       {
3352         InitJoysticks();
3353
3354         setup_mode = SETUP_MODE_MAIN;
3355         DrawSetupScreen();
3356       }
3357     }
3358   }
3359 }
3360
3361 void CustomizeKeyboard(int player_nr)
3362 {
3363   int i;
3364   int step_nr;
3365   boolean finished = FALSE;
3366   static struct SetupKeyboardInfo custom_key;
3367   static struct
3368   {
3369     Key *key;
3370     char *text;
3371   } customize_step[] =
3372   {
3373     { &custom_key.left,  "Move Left"    },
3374     { &custom_key.right, "Move Right"   },
3375     { &custom_key.up,    "Move Up"      },
3376     { &custom_key.down,  "Move Down"    },
3377     { &custom_key.snap,  "Snap Field"   },
3378     { &custom_key.drop,  "Drop Element" }
3379   };
3380
3381   /* read existing key bindings from player setup */
3382   custom_key = setup.input[player_nr].key;
3383
3384   ClearWindow();
3385
3386 #if 1
3387   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
3388 #else
3389   DrawText(mSX + 16, mSY + 16, "Keyboard Input", FONT_TITLE_1);
3390 #endif
3391
3392   BackToFront();
3393   InitAnimation();
3394
3395   step_nr = 0;
3396   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
3397            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
3398   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
3399            "Key:", FONT_INPUT_1_ACTIVE);
3400   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3401            getKeyNameFromKey(*customize_step[step_nr].key), FONT_VALUE_OLD);
3402
3403   while (!finished)
3404   {
3405     if (PendingEvent())         /* got event */
3406     {
3407       Event event;
3408
3409       NextEvent(&event);
3410
3411       switch(event.type)
3412       {
3413         case EVENT_KEYPRESS:
3414           {
3415             Key key = GetEventKey((KeyEvent *)&event, FALSE);
3416
3417             if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
3418             {
3419               finished = TRUE;
3420               break;
3421             }
3422
3423             /* all keys configured -- wait for "Escape" or "Return" key */
3424             if (step_nr == 6)
3425               break;
3426
3427             /* press 'Enter' to keep the existing key binding */
3428             if (key == KSYM_Return)
3429               key = *customize_step[step_nr].key;
3430
3431             /* check if key already used */
3432             for (i = 0; i < step_nr; i++)
3433               if (*customize_step[i].key == key)
3434                 break;
3435             if (i < step_nr)
3436               break;
3437
3438             /* got new key binding */
3439             *customize_step[step_nr].key = key;
3440             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3441                      "             ", FONT_VALUE_1);
3442             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3443                      getKeyNameFromKey(key), FONT_VALUE_1);
3444             step_nr++;
3445
3446             /* un-highlight last query */
3447             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
3448                      customize_step[step_nr - 1].text, FONT_MENU_1);
3449             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
3450                      "Key:", FONT_MENU_1);
3451
3452             /* press 'Enter' to leave */
3453             if (step_nr == 6)
3454             {
3455               DrawText(mSX + 16, mSY + 15 * 32 + 16,
3456                        "Press Enter", FONT_TITLE_1);
3457               break;
3458             }
3459
3460             /* query next key binding */
3461             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
3462                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
3463             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
3464                      "Key:", FONT_INPUT_1_ACTIVE);
3465             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3466                      getKeyNameFromKey(*customize_step[step_nr].key),
3467                      FONT_VALUE_OLD);
3468           }
3469           break;
3470
3471         case EVENT_KEYRELEASE:
3472           key_joystick_mapping = 0;
3473           break;
3474
3475         default:
3476           HandleOtherEvents(&event);
3477           break;
3478       }
3479     }
3480
3481     DoAnimation();
3482     BackToFront();
3483
3484     /* don't eat all CPU time */
3485     Delay(10);
3486   }
3487
3488   /* write new key bindings back to player setup */
3489   setup.input[player_nr].key = custom_key;
3490
3491   StopAnimation();
3492   DrawSetupScreen_Input();
3493 }
3494
3495 static boolean CalibrateJoystickMain(int player_nr)
3496 {
3497   int new_joystick_xleft = JOYSTICK_XMIDDLE;
3498   int new_joystick_xright = JOYSTICK_XMIDDLE;
3499   int new_joystick_yupper = JOYSTICK_YMIDDLE;
3500   int new_joystick_ylower = JOYSTICK_YMIDDLE;
3501   int new_joystick_xmiddle, new_joystick_ymiddle;
3502
3503   int joystick_fd = joystick.fd[player_nr];
3504   int x, y, last_x, last_y, xpos = 8, ypos = 3;
3505   boolean check[3][3];
3506   int check_remaining = 3 * 3;
3507   int joy_x, joy_y;
3508   int joy_value;
3509   int result = -1;
3510
3511   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
3512     return FALSE;
3513
3514   if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
3515     return FALSE;
3516
3517   ClearWindow();
3518
3519   for (y = 0; y < 3; y++)
3520   {
3521     for (x = 0; x < 3; x++)
3522     {
3523       DrawGraphic(xpos + x - 1, ypos + y - 1, IMG_MENU_CALIBRATE_BLUE, 0);
3524       check[x][y] = FALSE;
3525     }
3526   }
3527
3528   DrawTextSCentered(mSY - SY +  6 * 32, FONT_TITLE_1, "Rotate joystick");
3529   DrawTextSCentered(mSY - SY +  7 * 32, FONT_TITLE_1, "in all directions");
3530   DrawTextSCentered(mSY - SY +  9 * 32, FONT_TITLE_1, "if all balls");
3531   DrawTextSCentered(mSY - SY + 10 * 32, FONT_TITLE_1, "are marked,");
3532   DrawTextSCentered(mSY - SY + 11 * 32, FONT_TITLE_1, "center joystick");
3533   DrawTextSCentered(mSY - SY + 12 * 32, FONT_TITLE_1, "and");
3534   DrawTextSCentered(mSY - SY + 13 * 32, FONT_TITLE_1, "press any button!");
3535
3536   joy_value = Joystick(player_nr);
3537   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
3538   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
3539
3540   /* eventually uncalibrated center position (joystick could be uncentered) */
3541   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3542     return FALSE;
3543
3544   new_joystick_xmiddle = joy_x;
3545   new_joystick_ymiddle = joy_y;
3546
3547   DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_RED, 0);
3548   BackToFront();
3549
3550   while (Joystick(player_nr) & JOY_BUTTON);     /* wait for released button */
3551   InitAnimation();
3552
3553   while (result < 0)
3554   {
3555     if (PendingEvent())         /* got event */
3556     {
3557       Event event;
3558
3559       NextEvent(&event);
3560
3561       switch(event.type)
3562       {
3563         case EVENT_KEYPRESS:
3564           switch(GetEventKey((KeyEvent *)&event, TRUE))
3565           {
3566             case KSYM_Return:
3567               if (check_remaining == 0)
3568                 result = 1;
3569               break;
3570
3571             case KSYM_Escape:
3572               result = 0;
3573               break;
3574
3575             default:
3576               break;
3577           }
3578           break;
3579
3580         case EVENT_KEYRELEASE:
3581           key_joystick_mapping = 0;
3582           break;
3583
3584         default:
3585           HandleOtherEvents(&event);
3586           break;
3587       }
3588     }
3589
3590     if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3591       return FALSE;
3592
3593     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
3594     new_joystick_xright = MAX(new_joystick_xright, joy_x);
3595     new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
3596     new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
3597
3598     setup.input[player_nr].joy.xleft = new_joystick_xleft;
3599     setup.input[player_nr].joy.yupper = new_joystick_yupper;
3600     setup.input[player_nr].joy.xright = new_joystick_xright;
3601     setup.input[player_nr].joy.ylower = new_joystick_ylower;
3602     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
3603     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
3604
3605     CheckJoystickData();
3606
3607     joy_value = Joystick(player_nr);
3608
3609     if (joy_value & JOY_BUTTON && check_remaining == 0)
3610       result = 1;
3611
3612     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
3613     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
3614
3615     if (x != last_x || y != last_y)
3616     {
3617       DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_YELLOW, 0);
3618       DrawGraphic(xpos + x,      ypos + y,      IMG_MENU_CALIBRATE_RED,    0);
3619
3620       last_x = x;
3621       last_y = y;
3622
3623       if (check_remaining > 0 && !check[x+1][y+1])
3624       {
3625         check[x+1][y+1] = TRUE;
3626         check_remaining--;
3627       }
3628
3629 #if 0
3630 #ifdef DEBUG
3631       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
3632              setup.input[player_nr].joy.xleft,
3633              setup.input[player_nr].joy.xmiddle,
3634              setup.input[player_nr].joy.xright);
3635       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
3636              setup.input[player_nr].joy.yupper,
3637              setup.input[player_nr].joy.ymiddle,
3638              setup.input[player_nr].joy.ylower);
3639 #endif
3640 #endif
3641
3642     }
3643
3644     DoAnimation();
3645     BackToFront();
3646
3647     /* don't eat all CPU time */
3648     Delay(10);
3649   }
3650
3651   /* calibrated center position (joystick should now be centered) */
3652   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3653     return FALSE;
3654
3655   new_joystick_xmiddle = joy_x;
3656   new_joystick_ymiddle = joy_y;
3657
3658   StopAnimation();
3659
3660 #if 0
3661   DrawSetupScreen_Input();
3662 #endif
3663
3664   /* wait until the last pressed button was released */
3665   while (Joystick(player_nr) & JOY_BUTTON)
3666   {
3667     if (PendingEvent())         /* got event */
3668     {
3669       Event event;
3670
3671       NextEvent(&event);
3672       HandleOtherEvents(&event);
3673
3674       Delay(10);
3675     }
3676   }
3677
3678   return TRUE;
3679 }
3680
3681 void CalibrateJoystick(int player_nr)
3682 {
3683   if (!CalibrateJoystickMain(player_nr))
3684   {
3685     char *device_name = setup.input[player_nr].joy.device_name;
3686     int nr = getJoystickNrFromDeviceName(device_name) + 1;
3687     int xpos = mSX - SX;
3688     int ypos = mSY - SY;
3689
3690     ClearWindow();
3691
3692     DrawTextF(xpos + 16, ypos + 6 * 32, FONT_TITLE_1, "   JOYSTICK %d   ", nr);
3693     DrawTextF(xpos + 16, ypos + 7 * 32, FONT_TITLE_1, " NOT AVAILABLE! ");
3694     BackToFront();
3695
3696     Delay(2000);                /* show error message for a short time */
3697
3698     ClearEventQueue();
3699   }
3700
3701 #if 1
3702   DrawSetupScreen_Input();
3703 #endif
3704 }
3705
3706 void DrawSetupScreen()
3707 {
3708   DeactivateJoystick();
3709
3710   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
3711
3712   if (setup_mode == SETUP_MODE_INPUT)
3713     DrawSetupScreen_Input();
3714   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
3715     DrawChooseTree(&artwork.gfx_current);
3716   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
3717     DrawChooseTree(&artwork.snd_current);
3718   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
3719     DrawChooseTree(&artwork.mus_current);
3720   else
3721     DrawSetupScreen_Generic();
3722
3723   PlayMenuSound();
3724   PlayMenuMusic();
3725 }
3726
3727 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
3728 {
3729   if (setup_mode == SETUP_MODE_INPUT)
3730     HandleSetupScreen_Input(mx, my, dx, dy, button);
3731   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
3732     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
3733   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
3734     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
3735   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
3736     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
3737   else
3738     HandleSetupScreen_Generic(mx, my, dx, dy, button);
3739
3740   DoAnimation();
3741 }
3742
3743 void HandleGameActions()
3744 {
3745   if (game_status != GAME_MODE_PLAYING)
3746     return;
3747
3748   GameActions();        /* main game loop */
3749
3750   if (tape.auto_play && !tape.playing)
3751     AutoPlayTape();     /* continue automatically playing next tape */
3752 }
3753
3754
3755 /* ---------- new screen button stuff -------------------------------------- */
3756
3757 static struct
3758 {
3759   int gfx_unpressed, gfx_pressed;
3760   int x, y;
3761   int gadget_id;
3762   char *infotext;
3763 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
3764 {
3765   {
3766     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
3767     SC_SCROLL_UP_XPOS, SC_SCROLL_UP_YPOS,
3768     SCREEN_CTRL_ID_SCROLL_UP,
3769     "scroll up"
3770   },
3771   {
3772     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
3773     SC_SCROLL_DOWN_XPOS, SC_SCROLL_DOWN_YPOS,
3774     SCREEN_CTRL_ID_SCROLL_DOWN,
3775     "scroll down"
3776   }
3777 };
3778
3779 static struct
3780 {
3781 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3782   Bitmap **gfx_unpressed, **gfx_pressed;
3783 #else
3784   int gfx_unpressed, gfx_pressed;
3785 #endif
3786   int x, y;
3787   int width, height;
3788   int type;
3789   int gadget_id;
3790   char *infotext;
3791 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
3792 {
3793   {
3794 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3795     &scrollbar_bitmap[0], &scrollbar_bitmap[1],
3796 #else
3797     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
3798 #endif
3799     SC_SCROLL_VERTICAL_XPOS, SC_SCROLL_VERTICAL_YPOS,
3800     SC_SCROLL_VERTICAL_XSIZE, SC_SCROLL_VERTICAL_YSIZE,
3801     GD_TYPE_SCROLLBAR_VERTICAL,
3802     SCREEN_CTRL_ID_SCROLL_VERTICAL,
3803     "scroll level series vertically"
3804   }
3805 };
3806
3807 static void CreateScreenScrollbuttons()
3808 {
3809   struct GadgetInfo *gi;
3810   unsigned long event_mask;
3811   int i;
3812
3813   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
3814   {
3815     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
3816     int gfx_unpressed, gfx_pressed;
3817     int x, y, width, height;
3818     int gd_x1, gd_x2, gd_y1, gd_y2;
3819     int id = scrollbutton_info[i].gadget_id;
3820
3821     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
3822
3823     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
3824     y = mSY + scrollbutton_info[i].y;
3825     width = SC_SCROLLBUTTON_XSIZE;
3826     height = SC_SCROLLBUTTON_YSIZE;
3827
3828     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
3829       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
3830                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
3831
3832     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
3833     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
3834     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
3835     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
3836     gd_x1 = graphic_info[gfx_unpressed].src_x;
3837     gd_y1 = graphic_info[gfx_unpressed].src_y;
3838     gd_x2 = graphic_info[gfx_pressed].src_x;
3839     gd_y2 = graphic_info[gfx_pressed].src_y;
3840
3841     gi = CreateGadget(GDI_CUSTOM_ID, id,
3842                       GDI_CUSTOM_TYPE_ID, i,
3843                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
3844                       GDI_X, x,
3845                       GDI_Y, y,
3846                       GDI_WIDTH, width,
3847                       GDI_HEIGHT, height,
3848                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
3849                       GDI_STATE, GD_BUTTON_UNPRESSED,
3850                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
3851                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
3852                       GDI_DIRECT_DRAW, FALSE,
3853                       GDI_EVENT_MASK, event_mask,
3854                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
3855                       GDI_END);
3856
3857     if (gi == NULL)
3858       Error(ERR_EXIT, "cannot create gadget");
3859
3860     screen_gadget[id] = gi;
3861   }
3862 }
3863
3864 static void CreateScreenScrollbars()
3865 {
3866   int i;
3867
3868   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
3869   {
3870     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
3871 #if !defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3872     int gfx_unpressed, gfx_pressed;
3873 #endif
3874     int x, y, width, height;
3875     int gd_x1, gd_x2, gd_y1, gd_y2;
3876     struct GadgetInfo *gi;
3877     int items_max, items_visible, item_position;
3878     unsigned long event_mask;
3879     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
3880     int id = scrollbar_info[i].gadget_id;
3881
3882     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
3883
3884     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
3885     y = mSY + scrollbar_info[i].y;
3886     width  = scrollbar_info[i].width;
3887     height = scrollbar_info[i].height;
3888
3889     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
3890       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
3891
3892     items_max = num_page_entries;
3893     items_visible = num_page_entries;
3894     item_position = 0;
3895
3896 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3897     gd_bitmap_unpressed = *scrollbar_info[i].gfx_unpressed;
3898     gd_bitmap_pressed   = *scrollbar_info[i].gfx_pressed;
3899     gd_x1 = 0;
3900     gd_y1 = 0;
3901     gd_x2 = 0;
3902     gd_y2 = 0;
3903 #else
3904     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
3905     gfx_pressed   = scrollbar_info[i].gfx_pressed;
3906     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
3907     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
3908     gd_x1 = graphic_info[gfx_unpressed].src_x;
3909     gd_y1 = graphic_info[gfx_unpressed].src_y;
3910     gd_x2 = graphic_info[gfx_pressed].src_x;
3911     gd_y2 = graphic_info[gfx_pressed].src_y;
3912 #endif
3913
3914     gi = CreateGadget(GDI_CUSTOM_ID, id,
3915                       GDI_CUSTOM_TYPE_ID, i,
3916                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
3917                       GDI_X, x,
3918                       GDI_Y, y,
3919                       GDI_WIDTH, width,
3920                       GDI_HEIGHT, height,
3921                       GDI_TYPE, scrollbar_info[i].type,
3922                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
3923                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
3924                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
3925                       GDI_STATE, GD_BUTTON_UNPRESSED,
3926                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
3927                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
3928                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
3929                       GDI_DIRECT_DRAW, FALSE,
3930                       GDI_EVENT_MASK, event_mask,
3931                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
3932                       GDI_END);
3933
3934     if (gi == NULL)
3935       Error(ERR_EXIT, "cannot create gadget");
3936
3937     screen_gadget[id] = gi;
3938   }
3939 }
3940
3941 void CreateScreenGadgets()
3942 {
3943   int last_game_status = game_status;   /* save current game status */
3944
3945 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3946   int i;
3947
3948   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
3949   {
3950     scrollbar_bitmap[i] = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
3951
3952     /* copy pointers to clip mask and GC */
3953     scrollbar_bitmap[i]->clip_mask =
3954       graphic_info[IMG_MENU_SCROLLBAR + i].clip_mask;
3955     scrollbar_bitmap[i]->stored_clip_gc =
3956       graphic_info[IMG_MENU_SCROLLBAR + i].clip_gc;
3957
3958     BlitBitmap(graphic_info[IMG_MENU_SCROLLBAR + i].bitmap,
3959                scrollbar_bitmap[i],
3960                graphic_info[IMG_MENU_SCROLLBAR + i].src_x,
3961                graphic_info[IMG_MENU_SCROLLBAR + i].src_y,
3962                TILEX, TILEY, 0, 0);
3963   }
3964 #endif
3965
3966   /* force LEVELS draw offset for scrollbar / scrollbutton gadgets */
3967   game_status = GAME_MODE_LEVELS;
3968
3969   CreateScreenScrollbuttons();
3970   CreateScreenScrollbars();
3971
3972   game_status = last_game_status;       /* restore current game status */
3973 }
3974
3975 void FreeScreenGadgets()
3976 {
3977   int i;
3978
3979 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3980   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
3981   {
3982     /* prevent freeing clip mask and GC twice */
3983     scrollbar_bitmap[i]->clip_mask = None;
3984     scrollbar_bitmap[i]->stored_clip_gc = None;
3985
3986     FreeBitmap(scrollbar_bitmap[i]);
3987   }
3988 #endif
3989
3990   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
3991     FreeGadget(screen_gadget[i]);
3992 }
3993
3994 void MapChooseTreeGadgets(TreeInfo *ti)
3995 {
3996   int num_entries = numTreeInfoInGroup(ti);
3997   int i;
3998
3999   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
4000     return;
4001
4002   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
4003     MapGadget(screen_gadget[i]);
4004 }
4005
4006 #if 0
4007 void UnmapChooseTreeGadgets()
4008 {
4009   int i;
4010
4011   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
4012     UnmapGadget(screen_gadget[i]);
4013 }
4014 #endif
4015
4016 static void HandleScreenGadgets(struct GadgetInfo *gi)
4017 {
4018   int id = gi->custom_id;
4019
4020   if (game_status != GAME_MODE_LEVELS && game_status != GAME_MODE_SETUP)
4021     return;
4022
4023   switch (id)
4024   {
4025     case SCREEN_CTRL_ID_SCROLL_UP:
4026       if (game_status == GAME_MODE_LEVELS)
4027         HandleChooseLevel(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
4028       else if (game_status == GAME_MODE_SETUP)
4029         HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
4030       break;
4031
4032     case SCREEN_CTRL_ID_SCROLL_DOWN:
4033       if (game_status == GAME_MODE_LEVELS)
4034         HandleChooseLevel(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4035       else if (game_status == GAME_MODE_SETUP)
4036         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4037       break;
4038
4039     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
4040       if (game_status == GAME_MODE_LEVELS)
4041         HandleChooseLevel(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4042       else if (game_status == GAME_MODE_SETUP)
4043         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4044       break;
4045
4046     default:
4047       break;
4048   }
4049 }