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