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