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