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