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