rnd-20060802-2-src
[rocksndiamonds.git] / src / screens.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * screens.c                                                *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "screens.h"
17 #include "events.h"
18 #include "game.h"
19 #include "tools.h"
20 #include "editor.h"
21 #include "files.h"
22 #include "tape.h"
23 #include "cartoons.h"
24 #include "network.h"
25 #include "init.h"
26
27 /* screens in the setup menu */
28 #define SETUP_MODE_MAIN                 0
29 #define SETUP_MODE_GAME                 1
30 #define SETUP_MODE_EDITOR               2
31 #define SETUP_MODE_INPUT                3
32 #define SETUP_MODE_SHORTCUT_1           4
33 #define SETUP_MODE_SHORTCUT_2           5
34 #define SETUP_MODE_GRAPHICS             6
35 #define SETUP_MODE_CHOOSE_SCREEN_MODE   7
36 #define SETUP_MODE_SOUND                8
37 #define SETUP_MODE_ARTWORK              9
38 #define SETUP_MODE_CHOOSE_GRAPHICS      10
39 #define SETUP_MODE_CHOOSE_SOUNDS        11
40 #define SETUP_MODE_CHOOSE_MUSIC         12
41
42 #define MAX_SETUP_MODES                 13
43
44 /* for input setup functions */
45 #define SETUPINPUT_SCREEN_POS_START     0
46 #define SETUPINPUT_SCREEN_POS_END       (SCR_FIELDY - 4)
47 #define SETUPINPUT_SCREEN_POS_EMPTY1    (SETUPINPUT_SCREEN_POS_START + 3)
48 #define SETUPINPUT_SCREEN_POS_EMPTY2    (SETUPINPUT_SCREEN_POS_END - 1)
49
50 /* screens on the info screen */
51 #define INFO_MODE_MAIN                  0
52 #define INFO_MODE_TITLE                 1
53 #define INFO_MODE_ELEMENTS              2
54 #define INFO_MODE_MUSIC                 3
55 #define INFO_MODE_CREDITS               4
56 #define INFO_MODE_PROGRAM               5
57 #define INFO_MODE_LEVELSET              6
58
59 #define MAX_INFO_MODES                  7
60
61 /* for various menu stuff  */
62 #define MENU_SCREEN_START_XPOS          1
63 #define MENU_SCREEN_START_YPOS          2
64 #define MENU_SCREEN_VALUE_XPOS          14
65 #define MENU_SCREEN_MAX_XPOS            (SCR_FIELDX - 1)
66 #define MENU_TITLE1_YPOS                8
67 #define MENU_TITLE2_YPOS                46
68 #define MAX_INFO_ELEMENTS_ON_SCREEN     10
69 #define MAX_MENU_ENTRIES_ON_SCREEN      (SCR_FIELDY - MENU_SCREEN_START_YPOS)
70 #define MAX_MENU_TEXT_LENGTH_BIG        (MENU_SCREEN_VALUE_XPOS -       \
71                                          MENU_SCREEN_START_XPOS)
72 #define MAX_MENU_TEXT_LENGTH_MEDIUM     (MAX_MENU_TEXT_LENGTH_BIG * 2)
73
74 /* buttons and scrollbars identifiers */
75 #define SCREEN_CTRL_ID_LAST_LEVEL       0
76 #define SCREEN_CTRL_ID_NEXT_LEVEL       1
77 #define SCREEN_CTRL_ID_LAST_PLAYER      2
78 #define SCREEN_CTRL_ID_NEXT_PLAYER      3
79 #define SCREEN_CTRL_ID_SCROLL_UP        4
80 #define SCREEN_CTRL_ID_SCROLL_DOWN      5
81 #define SCREEN_CTRL_ID_SCROLL_VERTICAL  6
82
83 #define NUM_SCREEN_GADGETS              7
84
85 #define NUM_SCREEN_MENUBUTTONS          4
86 #define NUM_SCREEN_SCROLLBUTTONS        2
87 #define NUM_SCREEN_SCROLLBARS           1
88
89 #define SCREEN_MASK_MAIN                (1 << 0)
90 #define SCREEN_MASK_INPUT               (1 << 1)
91
92 /* graphic position and size values for buttons and scrollbars */
93 #define SC_MENUBUTTON_XSIZE             TILEX
94 #define SC_MENUBUTTON_YSIZE             TILEY
95
96 #define SC_SCROLLBUTTON_XSIZE           TILEX
97 #define SC_SCROLLBUTTON_YSIZE           TILEY
98
99 #define SC_SCROLLBAR_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
100
101 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
102 #define SC_SCROLL_VERTICAL_YSIZE        ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \
103                                          SC_SCROLLBUTTON_YSIZE)
104
105 #define SC_SCROLL_UP_XPOS               SC_SCROLLBAR_XPOS
106 #define SC_SCROLL_UP_YPOS               (2 * SC_SCROLLBUTTON_YSIZE)
107
108 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLLBAR_XPOS
109 #define SC_SCROLL_VERTICAL_YPOS         (SC_SCROLL_UP_YPOS + \
110                                          SC_SCROLLBUTTON_YSIZE)
111
112 #define SC_SCROLL_DOWN_XPOS             SC_SCROLLBAR_XPOS
113 #define SC_SCROLL_DOWN_YPOS             (SC_SCROLL_VERTICAL_YPOS + \
114                                          SC_SCROLL_VERTICAL_YSIZE)
115
116 #define SC_BORDER_SIZE                  14
117
118
119 /* forward declarations of internal functions */
120 static void HandleScreenGadgets(struct GadgetInfo *);
121 static void HandleSetupScreen_Generic(int, int, int, int, int);
122 static void HandleSetupScreen_Input(int, int, int, int, int);
123 static void CustomizeKeyboard(int);
124 static void CalibrateJoystick(int);
125 static void execSetupGraphics(void);
126 static void execSetupArtwork(void);
127 static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
128
129 static void DrawChooseLevel(void);
130 static void DrawInfoScreen(void);
131 static void DrawSetupScreen(void);
132
133 static void DrawInfoScreenExt(int);
134 static void DrawInfoScreen_NotAvailable(char *, char *);
135 static void DrawInfoScreen_HelpAnim(int, int, boolean);
136 static void DrawInfoScreen_HelpText(int, int, int, int);
137 static void HandleInfoScreen_Main(int, int, int, int, int);
138 static void HandleInfoScreen_TitleScreen(int);
139 static void HandleInfoScreen_Elements(int);
140 static void HandleInfoScreen_Music(int);
141 static void HandleInfoScreen_Credits(int);
142 static void HandleInfoScreen_Program(int);
143
144 static void MapScreenMenuGadgets(int);
145 static void MapScreenTreeGadgets(TreeInfo *);
146
147 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
148 static int setup_mode = SETUP_MODE_MAIN;
149 static int info_mode = INFO_MODE_MAIN;
150
151 static TreeInfo *screen_modes = NULL;
152 static TreeInfo *screen_mode_current = NULL;
153
154 #define DRAW_OFFSET_MODE(x)     (x >= GAME_MODE_MAIN &&                 \
155                                  x <= GAME_MODE_SETUP ? x :             \
156                                  x == GAME_MODE_PSEUDO_TYPENAME ?       \
157                                  GAME_MODE_MAIN : GAME_MODE_DEFAULT)
158
159 #define mSX (SX + menu.draw_xoffset[DRAW_OFFSET_MODE(game_status)])
160 #define mSY (SY + menu.draw_yoffset[DRAW_OFFSET_MODE(game_status)])
161
162 #define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ?   \
163                                     menu.list_size[game_status] :       \
164                                     MAX_MENU_ENTRIES_ON_SCREEN)
165
166 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
167 #define NUM_SCROLLBAR_BITMAPS           2
168 static Bitmap *scrollbar_bitmap[NUM_SCROLLBAR_BITMAPS];
169 #endif
170
171
172 static void drawCursorExt(int xpos, int ypos, int color, int g)
173 {
174   static int cursor_array[SCR_FIELDY];
175
176   if (xpos == 0)
177   {
178     if (g != 0)
179       cursor_array[ypos] = g;
180     else
181       g = cursor_array[ypos];
182   }
183
184   if (color == FC_RED)
185     g = (g == IMG_MENU_BUTTON_LEFT  ? IMG_MENU_BUTTON_LEFT_ACTIVE  :
186          g == IMG_MENU_BUTTON_RIGHT ? IMG_MENU_BUTTON_RIGHT_ACTIVE :
187          g == IMG_MENU_BUTTON_LEAVE_MENU ? IMG_MENU_BUTTON_LEAVE_MENU_ACTIVE :
188          g == IMG_MENU_BUTTON_ENTER_MENU ? IMG_MENU_BUTTON_ENTER_MENU_ACTIVE :
189          g == IMG_MENU_BUTTON_LAST_LEVEL ? IMG_MENU_BUTTON_LAST_LEVEL_ACTIVE :
190          g == IMG_MENU_BUTTON_NEXT_LEVEL ? IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE :
191          IMG_MENU_BUTTON_ACTIVE);
192
193   ypos += MENU_SCREEN_START_YPOS;
194
195   DrawBackground(mSX + xpos * TILEX, mSY + ypos * TILEY, TILEX, TILEY);
196   DrawGraphicThruMaskExt(drawto, mSX + xpos * TILEX, mSY + ypos * TILEY, g, 0);
197 }
198
199 static void initCursor(int ypos, int graphic)
200 {
201   drawCursorExt(0, ypos, FC_BLUE, graphic);
202 }
203
204 static void drawCursor(int ypos, int color)
205 {
206   drawCursorExt(0, ypos, color, 0);
207 }
208
209 static void drawCursorXY(int xpos, int ypos, int graphic)
210 {
211   drawCursorExt(xpos, ypos, -1, graphic);
212 }
213
214 static void drawChooseTreeCursor(int ypos, int color)
215 {
216   int last_game_status = game_status;   /* save current game status */
217
218   /* force LEVELS draw offset on artwork setup screen */
219   game_status = GAME_MODE_LEVELS;
220
221   drawCursorExt(0, ypos, color, 0);
222
223   game_status = last_game_status;       /* restore current game status */
224 }
225
226 void DrawHeadline()
227 {
228   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, PROGRAM_TITLE_STRING);
229   DrawTextSCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, PROGRAM_COPYRIGHT_STRING);
230 }
231
232 static 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 : TYPE_ENTER);
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
3196     return;
3197   }
3198   else if (button == MB_MENU_LEAVE)
3199   {
3200     for (y = 0; y < num_setup_info; y++)
3201     {
3202       if (setup_info[y].type & TYPE_LEAVE_MENU)
3203       {
3204         void (*menu_callback_function)(void) = setup_info[y].value;
3205
3206         menu_callback_function();
3207
3208         break;  /* absolutely needed because function changes 'setup_info'! */
3209       }
3210     }
3211
3212     return;
3213   }
3214
3215   if (mx || my)         /* mouse input */
3216   {
3217     x = (mx - mSX) / 32;
3218     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3219   }
3220   else if (dx || dy)    /* keyboard input */
3221   {
3222     if (dx)
3223     {
3224       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
3225
3226       if (setup_info[choice].type & menu_navigation_type ||
3227           setup_info[choice].type & TYPE_BOOLEAN_STYLE)
3228         button = MB_MENU_CHOICE;
3229     }
3230     else if (dy)
3231       y = choice + dy;
3232
3233     /* jump to next non-empty menu entry (up or down) */
3234     while (y > 0 && y < num_setup_info - 1 &&
3235            setup_info[y].type & TYPE_SKIP_ENTRY)
3236       y += dy;
3237   }
3238
3239 #if 1
3240   if (IN_VIS_FIELD(x, y) && y >= 0 && y < num_setup_info)
3241   {
3242     if (button)
3243     {
3244       if (y != choice && setup_info[y].type & ~TYPE_SKIP_ENTRY)
3245       {
3246         drawCursor(y, FC_RED);
3247         drawCursor(choice, FC_BLUE);
3248         choice = choice_store[setup_mode] = y;
3249       }
3250     }
3251     else if (!(setup_info[y].type & TYPE_GHOSTED))
3252     {
3253       /* when selecting key headline, execute function for key value change */
3254       if (setup_info[y].type & TYPE_KEYTEXT &&
3255           setup_info[y + 1].type & TYPE_KEY)
3256         y++;
3257
3258       /* when selecting string value, execute function for list selection */
3259       if (setup_info[y].type & TYPE_STRING && y > 0 &&
3260           setup_info[y - 1].type & TYPE_ENTER_LIST)
3261         y--;
3262
3263       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE)
3264       {
3265         void (*menu_callback_function)(void) = setup_info[y].value;
3266
3267         menu_callback_function();
3268       }
3269       else
3270       {
3271         if (setup_info[y].type & TYPE_VALUE)
3272           changeSetupValue(y);
3273       }
3274     }
3275   }
3276 #else
3277   if (IN_VIS_FIELD(x, y) &&
3278       y >= 0 && y < num_setup_info && setup_info[y].type & ~TYPE_SKIP_ENTRY)
3279   {
3280     if (button)
3281     {
3282       if (y != choice)
3283       {
3284         drawCursor(y, FC_RED);
3285         drawCursor(choice, FC_BLUE);
3286         choice = choice_store[setup_mode] = y;
3287       }
3288     }
3289     else if (!(setup_info[y].type & TYPE_GHOSTED))
3290     {
3291       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE)
3292       {
3293         void (*menu_callback_function)(void) = setup_info[choice].value;
3294
3295         menu_callback_function();
3296       }
3297       else
3298       {
3299         if (setup_info[y].type & TYPE_KEYTEXT &&
3300             setup_info[y + 1].type & TYPE_KEY)
3301           y++;
3302
3303         if (setup_info[y].type & TYPE_VALUE)
3304           changeSetupValue(y);
3305       }
3306     }
3307   }
3308 #endif
3309 }
3310
3311 void DrawSetupScreen_Input()
3312 {
3313   ClearWindow();
3314
3315 #if 1
3316   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Setup Input");
3317 #else
3318   DrawText(mSX + 16, mSY + 16, "Setup Input", FONT_TITLE_1);
3319 #endif
3320
3321   initCursor(0,  IMG_MENU_BUTTON);
3322   initCursor(1,  IMG_MENU_BUTTON);
3323   initCursor(2,  IMG_MENU_BUTTON_ENTER_MENU);
3324   initCursor(13, IMG_MENU_BUTTON_LEAVE_MENU);
3325
3326 #if 0
3327   drawCursorXY(10, 0, IMG_MENU_BUTTON_LEFT);
3328   drawCursorXY(12, 0, IMG_MENU_BUTTON_RIGHT);
3329 #endif
3330
3331   DrawText(mSX + 32, mSY +  2 * 32, "Player:", FONT_MENU_1);
3332   DrawText(mSX + 32, mSY +  3 * 32, "Device:", FONT_MENU_1);
3333   DrawText(mSX + 32, mSY + 15 * 32, "Back",   FONT_MENU_1);
3334
3335 #if 0
3336   DeactivateJoystickForCalibration();
3337 #endif
3338 #if 1
3339   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3340                     "Joysticks deactivated on this screen");
3341 #endif
3342
3343 #if 1
3344   /* create gadgets for setup input menu screen */
3345   FreeScreenGadgets();
3346   CreateScreenGadgets();
3347
3348   /* map gadgets for setup input menu screen */
3349   MapScreenMenuGadgets(SCREEN_MASK_INPUT);
3350 #endif
3351
3352   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
3353   FadeToFront();
3354   InitAnimation();
3355 }
3356
3357 static void setJoystickDeviceToNr(char *device_name, int device_nr)
3358 {
3359   if (device_name == NULL)
3360     return;
3361
3362   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
3363     device_nr = 0;
3364
3365   if (strlen(device_name) > 1)
3366   {
3367     char c1 = device_name[strlen(device_name) - 1];
3368     char c2 = device_name[strlen(device_name) - 2];
3369
3370     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
3371       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
3372   }
3373   else
3374     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
3375             strlen(device_name));
3376 }
3377
3378 static void drawPlayerSetupInputInfo(int player_nr)
3379 {
3380   int i;
3381   static struct SetupKeyboardInfo custom_key;
3382   static struct
3383   {
3384     Key *key;
3385     char *text;
3386   } custom[] =
3387   {
3388     { &custom_key.left,  "Joystick Left"  },
3389     { &custom_key.right, "Joystick Right" },
3390     { &custom_key.up,    "Joystick Up"    },
3391     { &custom_key.down,  "Joystick Down"  },
3392     { &custom_key.snap,  "Button 1"       },
3393     { &custom_key.drop,  "Button 2"       }
3394   };
3395   static char *joystick_name[MAX_PLAYERS] =
3396   {
3397     "Joystick1",
3398     "Joystick2",
3399     "Joystick3",
3400     "Joystick4"
3401   };
3402
3403   InitJoysticks();
3404
3405   custom_key = setup.input[player_nr].key;
3406
3407   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
3408            FONT_INPUT_1_ACTIVE);
3409
3410   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3411                              TILEX, TILEY);
3412   DrawGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3413                          PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
3414
3415   if (setup.input[player_nr].use_joystick)
3416   {
3417     char *device_name = setup.input[player_nr].joy.device_name;
3418     char *text = joystick_name[getJoystickNrFromDeviceName(device_name)];
3419     int font_nr = (joystick.fd[player_nr] < 0 ? FONT_VALUE_OLD : FONT_VALUE_1);
3420
3421     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
3422     DrawText(mSX + 32, mSY + 4 * 32, "Calibrate", FONT_MENU_1);
3423   }
3424   else
3425   {
3426     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", FONT_VALUE_1);
3427     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", FONT_MENU_1);
3428   }
3429
3430   DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", FONT_MENU_1);
3431   drawCursorXY(1, 4, IMG_MENU_BUTTON_LEFT);
3432   drawCursorXY(1, 5, IMG_MENU_BUTTON_RIGHT);
3433   drawCursorXY(1, 6, IMG_MENU_BUTTON_UP);
3434   drawCursorXY(1, 7, IMG_MENU_BUTTON_DOWN);
3435   DrawText(mSX + 2 * 32, mSY +  6 * 32, ":", FONT_VALUE_OLD);
3436   DrawText(mSX + 2 * 32, mSY +  7 * 32, ":", FONT_VALUE_OLD);
3437   DrawText(mSX + 2 * 32, mSY +  8 * 32, ":", FONT_VALUE_OLD);
3438   DrawText(mSX + 2 * 32, mSY +  9 * 32, ":", FONT_VALUE_OLD);
3439   DrawText(mSX + 1 * 32, mSY + 10 * 32, "Snap Field:", FONT_VALUE_OLD);
3440   DrawText(mSX + 1 * 32, mSY + 12 * 32, "Drop Element:", FONT_VALUE_OLD);
3441
3442   for (i = 0; i < 6; i++)
3443   {
3444     int ypos = 6 + i + (i > 3 ? i-3 : 0);
3445
3446     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3447              "              ", FONT_VALUE_1);
3448     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3449              (setup.input[player_nr].use_joystick ?
3450               custom[i].text :
3451               getKeyNameFromKey(*custom[i].key)), FONT_VALUE_1);
3452   }
3453 }
3454
3455 static int input_player_nr = 0;
3456
3457 void HandleSetupScreen_Input_Player(int step, int direction)
3458 {
3459   int old_player_nr = input_player_nr;
3460   int new_player_nr;
3461
3462   new_player_nr = old_player_nr + step * direction;
3463   if (new_player_nr < 0)
3464     new_player_nr = 0;
3465   if (new_player_nr > MAX_PLAYERS - 1)
3466     new_player_nr = MAX_PLAYERS - 1;
3467
3468   if (new_player_nr != old_player_nr)
3469   {
3470     input_player_nr = new_player_nr;
3471
3472     drawPlayerSetupInputInfo(input_player_nr);
3473   }
3474 }
3475
3476 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
3477 {
3478   static int choice = 0;
3479   int x = 0;
3480   int y = choice;
3481   int pos_start  = SETUPINPUT_SCREEN_POS_START;
3482   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
3483   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
3484   int pos_end    = SETUPINPUT_SCREEN_POS_END;
3485
3486   if (button == MB_MENU_INITIALIZE)
3487   {
3488     drawPlayerSetupInputInfo(input_player_nr);
3489     drawCursor(choice, FC_RED);
3490
3491     return;
3492   }
3493   else if (button == MB_MENU_LEAVE)
3494   {
3495     setup_mode = SETUP_MODE_MAIN;
3496     DrawSetupScreen();
3497     InitJoysticks();
3498
3499     return;
3500   }
3501
3502   if (mx || my)         /* mouse input */
3503   {
3504     x = (mx - mSX) / 32;
3505     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3506   }
3507   else if (dx || dy)    /* keyboard input */
3508   {
3509     if (dx && choice == 0)
3510       x = (dx < 0 ? 10 : 12);
3511     else if ((dx && choice == 1) ||
3512              (dx == +1 && choice == 2) ||
3513              (dx == -1 && choice == pos_end))
3514       button = MB_MENU_CHOICE;
3515     else if (dy)
3516       y = choice + dy;
3517
3518     if (y >= pos_empty1 && y <= pos_empty2)
3519       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
3520   }
3521
3522 #if 1
3523
3524   if (y == 0 && dx != 0 && button)
3525   {
3526     HandleSetupScreen_Input_Player(1, dx < 0 ? -1 : +1);
3527   }
3528
3529 #else
3530
3531   if (IN_VIS_FIELD(x, y) &&
3532       y == 0 && ((x < 10 && !button) || ((x == 10 || x == 12) && button)))
3533   {
3534     static unsigned long delay = 0;
3535
3536     if (!DelayReached(&delay, GADGET_FRAME_DELAY))
3537       return;
3538
3539     input_player_nr =
3540       (input_player_nr + (x == 10 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
3541
3542     drawPlayerSetupInputInfo(input_player_nr);
3543   }
3544
3545 #endif
3546
3547   else if (IN_VIS_FIELD(x, y) &&
3548            y >= pos_start && y <= pos_end &&
3549            !(y >= pos_empty1 && y <= pos_empty2))
3550   {
3551     if (button)
3552     {
3553       if (y != choice)
3554       {
3555         drawCursor(y, FC_RED);
3556         drawCursor(choice, FC_BLUE);
3557         choice = y;
3558       }
3559     }
3560     else
3561     {
3562       if (y == 1)
3563       {
3564         char *device_name = setup.input[input_player_nr].joy.device_name;
3565
3566         if (!setup.input[input_player_nr].use_joystick)
3567         {
3568           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
3569
3570           setJoystickDeviceToNr(device_name, new_device_nr);
3571           setup.input[input_player_nr].use_joystick = TRUE;
3572         }
3573         else
3574         {
3575           int device_nr = getJoystickNrFromDeviceName(device_name);
3576           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
3577
3578           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
3579             setup.input[input_player_nr].use_joystick = FALSE;
3580           else
3581             setJoystickDeviceToNr(device_name, new_device_nr);
3582         }
3583
3584         drawPlayerSetupInputInfo(input_player_nr);
3585       }
3586       else if (y == 2)
3587       {
3588         if (setup.input[input_player_nr].use_joystick)
3589         {
3590           InitJoysticks();
3591           CalibrateJoystick(input_player_nr);
3592         }
3593         else
3594           CustomizeKeyboard(input_player_nr);
3595       }
3596       else if (y == pos_end)
3597       {
3598         InitJoysticks();
3599
3600         setup_mode = SETUP_MODE_MAIN;
3601         DrawSetupScreen();
3602       }
3603     }
3604   }
3605 }
3606
3607 void CustomizeKeyboard(int player_nr)
3608 {
3609   int i;
3610   int step_nr;
3611   boolean finished = FALSE;
3612   static struct SetupKeyboardInfo custom_key;
3613   static struct
3614   {
3615     Key *key;
3616     char *text;
3617   } customize_step[] =
3618   {
3619     { &custom_key.left,  "Move Left"    },
3620     { &custom_key.right, "Move Right"   },
3621     { &custom_key.up,    "Move Up"      },
3622     { &custom_key.down,  "Move Down"    },
3623     { &custom_key.snap,  "Snap Field"   },
3624     { &custom_key.drop,  "Drop Element" }
3625   };
3626
3627   /* read existing key bindings from player setup */
3628   custom_key = setup.input[player_nr].key;
3629
3630   ClearWindow();
3631
3632 #if 1
3633   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
3634 #else
3635   DrawText(mSX + 16, mSY + 16, "Keyboard Input", FONT_TITLE_1);
3636 #endif
3637
3638   BackToFront();
3639   InitAnimation();
3640
3641   step_nr = 0;
3642   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
3643            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
3644   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
3645            "Key:", FONT_INPUT_1_ACTIVE);
3646   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3647            getKeyNameFromKey(*customize_step[step_nr].key), FONT_VALUE_OLD);
3648
3649   while (!finished)
3650   {
3651     if (PendingEvent())         /* got event */
3652     {
3653       Event event;
3654
3655       NextEvent(&event);
3656
3657       switch(event.type)
3658       {
3659         case EVENT_KEYPRESS:
3660           {
3661             Key key = GetEventKey((KeyEvent *)&event, FALSE);
3662
3663             if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
3664             {
3665               finished = TRUE;
3666               break;
3667             }
3668
3669             /* all keys configured -- wait for "Escape" or "Return" key */
3670             if (step_nr == 6)
3671               break;
3672
3673             /* press 'Enter' to keep the existing key binding */
3674             if (key == KSYM_Return)
3675               key = *customize_step[step_nr].key;
3676
3677             /* check if key already used */
3678             for (i = 0; i < step_nr; i++)
3679               if (*customize_step[i].key == key)
3680                 break;
3681             if (i < step_nr)
3682               break;
3683
3684             /* got new key binding */
3685             *customize_step[step_nr].key = key;
3686             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3687                      "             ", FONT_VALUE_1);
3688             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3689                      getKeyNameFromKey(key), FONT_VALUE_1);
3690             step_nr++;
3691
3692             /* un-highlight last query */
3693             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
3694                      customize_step[step_nr - 1].text, FONT_MENU_1);
3695             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
3696                      "Key:", FONT_MENU_1);
3697
3698             /* press 'Enter' to leave */
3699             if (step_nr == 6)
3700             {
3701               DrawText(mSX + 16, mSY + 15 * 32 + 16,
3702                        "Press Enter", FONT_TITLE_1);
3703               break;
3704             }
3705
3706             /* query next key binding */
3707             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
3708                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
3709             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
3710                      "Key:", FONT_INPUT_1_ACTIVE);
3711             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3712                      getKeyNameFromKey(*customize_step[step_nr].key),
3713                      FONT_VALUE_OLD);
3714           }
3715           break;
3716
3717         case EVENT_KEYRELEASE:
3718           key_joystick_mapping = 0;
3719           break;
3720
3721         default:
3722           HandleOtherEvents(&event);
3723           break;
3724       }
3725     }
3726
3727     DoAnimation();
3728     BackToFront();
3729
3730     /* don't eat all CPU time */
3731     Delay(10);
3732   }
3733
3734   /* write new key bindings back to player setup */
3735   setup.input[player_nr].key = custom_key;
3736
3737   StopAnimation();
3738   DrawSetupScreen_Input();
3739 }
3740
3741 static boolean CalibrateJoystickMain(int player_nr)
3742 {
3743   int new_joystick_xleft = JOYSTICK_XMIDDLE;
3744   int new_joystick_xright = JOYSTICK_XMIDDLE;
3745   int new_joystick_yupper = JOYSTICK_YMIDDLE;
3746   int new_joystick_ylower = JOYSTICK_YMIDDLE;
3747   int new_joystick_xmiddle, new_joystick_ymiddle;
3748
3749   int joystick_fd = joystick.fd[player_nr];
3750   int x, y, last_x, last_y, xpos = 8, ypos = 3;
3751   boolean check[3][3];
3752   int check_remaining = 3 * 3;
3753   int joy_x, joy_y;
3754   int joy_value;
3755   int result = -1;
3756
3757   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
3758     return FALSE;
3759
3760   if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
3761     return FALSE;
3762
3763   ClearWindow();
3764
3765   for (y = 0; y < 3; y++)
3766   {
3767     for (x = 0; x < 3; x++)
3768     {
3769       DrawGraphic(xpos + x - 1, ypos + y - 1, IMG_MENU_CALIBRATE_BLUE, 0);
3770       check[x][y] = FALSE;
3771     }
3772   }
3773
3774   DrawTextSCentered(mSY - SY +  6 * 32, FONT_TITLE_1, "Rotate joystick");
3775   DrawTextSCentered(mSY - SY +  7 * 32, FONT_TITLE_1, "in all directions");
3776   DrawTextSCentered(mSY - SY +  9 * 32, FONT_TITLE_1, "if all balls");
3777   DrawTextSCentered(mSY - SY + 10 * 32, FONT_TITLE_1, "are marked,");
3778   DrawTextSCentered(mSY - SY + 11 * 32, FONT_TITLE_1, "center joystick");
3779   DrawTextSCentered(mSY - SY + 12 * 32, FONT_TITLE_1, "and");
3780   DrawTextSCentered(mSY - SY + 13 * 32, FONT_TITLE_1, "press any button!");
3781
3782   joy_value = Joystick(player_nr);
3783   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
3784   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
3785
3786   /* eventually uncalibrated center position (joystick could be uncentered) */
3787   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3788     return FALSE;
3789
3790   new_joystick_xmiddle = joy_x;
3791   new_joystick_ymiddle = joy_y;
3792
3793   DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_RED, 0);
3794   BackToFront();
3795
3796   while (Joystick(player_nr) & JOY_BUTTON);     /* wait for released button */
3797   InitAnimation();
3798
3799   while (result < 0)
3800   {
3801     if (PendingEvent())         /* got event */
3802     {
3803       Event event;
3804
3805       NextEvent(&event);
3806
3807       switch(event.type)
3808       {
3809         case EVENT_KEYPRESS:
3810           switch(GetEventKey((KeyEvent *)&event, TRUE))
3811           {
3812             case KSYM_Return:
3813               if (check_remaining == 0)
3814                 result = 1;
3815               break;
3816
3817             case KSYM_Escape:
3818               result = 0;
3819               break;
3820
3821             default:
3822               break;
3823           }
3824           break;
3825
3826         case EVENT_KEYRELEASE:
3827           key_joystick_mapping = 0;
3828           break;
3829
3830         default:
3831           HandleOtherEvents(&event);
3832           break;
3833       }
3834     }
3835
3836     if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3837       return FALSE;
3838
3839     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
3840     new_joystick_xright = MAX(new_joystick_xright, joy_x);
3841     new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
3842     new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
3843
3844     setup.input[player_nr].joy.xleft = new_joystick_xleft;
3845     setup.input[player_nr].joy.yupper = new_joystick_yupper;
3846     setup.input[player_nr].joy.xright = new_joystick_xright;
3847     setup.input[player_nr].joy.ylower = new_joystick_ylower;
3848     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
3849     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
3850
3851     CheckJoystickData();
3852
3853     joy_value = Joystick(player_nr);
3854
3855     if (joy_value & JOY_BUTTON && check_remaining == 0)
3856       result = 1;
3857
3858     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
3859     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
3860
3861     if (x != last_x || y != last_y)
3862     {
3863       DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_YELLOW, 0);
3864       DrawGraphic(xpos + x,      ypos + y,      IMG_MENU_CALIBRATE_RED,    0);
3865
3866       last_x = x;
3867       last_y = y;
3868
3869       if (check_remaining > 0 && !check[x+1][y+1])
3870       {
3871         check[x+1][y+1] = TRUE;
3872         check_remaining--;
3873       }
3874
3875 #if 0
3876 #ifdef DEBUG
3877       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
3878              setup.input[player_nr].joy.xleft,
3879              setup.input[player_nr].joy.xmiddle,
3880              setup.input[player_nr].joy.xright);
3881       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
3882              setup.input[player_nr].joy.yupper,
3883              setup.input[player_nr].joy.ymiddle,
3884              setup.input[player_nr].joy.ylower);
3885 #endif
3886 #endif
3887
3888     }
3889
3890     DoAnimation();
3891     BackToFront();
3892
3893     /* don't eat all CPU time */
3894     Delay(10);
3895   }
3896
3897   /* calibrated center position (joystick should now be centered) */
3898   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3899     return FALSE;
3900
3901   new_joystick_xmiddle = joy_x;
3902   new_joystick_ymiddle = joy_y;
3903
3904   StopAnimation();
3905
3906 #if 0
3907   DrawSetupScreen_Input();
3908 #endif
3909
3910   /* wait until the last pressed button was released */
3911   while (Joystick(player_nr) & JOY_BUTTON)
3912   {
3913     if (PendingEvent())         /* got event */
3914     {
3915       Event event;
3916
3917       NextEvent(&event);
3918       HandleOtherEvents(&event);
3919
3920       Delay(10);
3921     }
3922   }
3923
3924   return TRUE;
3925 }
3926
3927 void CalibrateJoystick(int player_nr)
3928 {
3929   if (!CalibrateJoystickMain(player_nr))
3930   {
3931     char *device_name = setup.input[player_nr].joy.device_name;
3932     int nr = getJoystickNrFromDeviceName(device_name) + 1;
3933     int xpos = mSX - SX;
3934     int ypos = mSY - SY;
3935
3936     ClearWindow();
3937
3938     DrawTextF(xpos + 16, ypos + 6 * 32, FONT_TITLE_1, "   JOYSTICK %d   ", nr);
3939     DrawTextF(xpos + 16, ypos + 7 * 32, FONT_TITLE_1, " NOT AVAILABLE! ");
3940     BackToFront();
3941
3942     Delay(2000);                /* show error message for a short time */
3943
3944     ClearEventQueue();
3945   }
3946
3947 #if 1
3948   DrawSetupScreen_Input();
3949 #endif
3950 }
3951
3952 void DrawSetupScreen()
3953 {
3954   DeactivateJoystick();
3955
3956   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
3957
3958   if (setup_mode == SETUP_MODE_INPUT)
3959     DrawSetupScreen_Input();
3960   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
3961     DrawChooseTree(&screen_mode_current);
3962   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
3963     DrawChooseTree(&artwork.gfx_current);
3964   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
3965     DrawChooseTree(&artwork.snd_current);
3966   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
3967     DrawChooseTree(&artwork.mus_current);
3968   else
3969     DrawSetupScreen_Generic();
3970
3971   PlayMenuSound();
3972   PlayMenuMusic();
3973 }
3974
3975 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
3976 {
3977   if (setup_mode == SETUP_MODE_INPUT)
3978     HandleSetupScreen_Input(mx, my, dx, dy, button);
3979   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
3980     HandleChooseTree(mx, my, dx, dy, button, &screen_mode_current);
3981   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
3982     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
3983   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
3984     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
3985   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
3986     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
3987   else
3988     HandleSetupScreen_Generic(mx, my, dx, dy, button);
3989
3990   DoAnimation();
3991 }
3992
3993 void HandleGameActions()
3994 {
3995   if (game_status != GAME_MODE_PLAYING)
3996     return;
3997
3998   GameActions();        /* main game loop */
3999
4000   if (tape.auto_play && !tape.playing)
4001     AutoPlayTape();     /* continue automatically playing next tape */
4002 }
4003
4004
4005 /* ---------- new screen button stuff -------------------------------------- */
4006
4007 static void getScreenMenuButtonPos(int *x, int *y, int gadget_id)
4008 {
4009   switch (gadget_id)
4010   {
4011     case SCREEN_CTRL_ID_LAST_LEVEL:
4012       *x = mSX + TILEX * getLastLevelButtonPos();
4013       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
4014       break;
4015
4016     case SCREEN_CTRL_ID_NEXT_LEVEL:
4017       *x = mSX + TILEX * getNextLevelButtonPos();
4018       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
4019       break;
4020
4021     case SCREEN_CTRL_ID_LAST_PLAYER:
4022       *x = mSX + TILEX * 10;
4023       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
4024       break;
4025
4026     case SCREEN_CTRL_ID_NEXT_PLAYER:
4027       *x = mSX + TILEX * 12;
4028       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
4029       break;
4030
4031     default:
4032       Error(ERR_EXIT, "unknown gadget ID %d", gadget_id);
4033   }
4034 }
4035
4036 static struct
4037 {
4038   int gfx_unpressed, gfx_pressed;
4039   void (*get_gadget_position)(int *, int *, int);
4040   int gadget_id;
4041   int screen_mask;
4042   char *infotext;
4043 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
4044 {
4045   {
4046     IMG_MENU_BUTTON_LAST_LEVEL, IMG_MENU_BUTTON_LAST_LEVEL_ACTIVE,
4047     getScreenMenuButtonPos,
4048     SCREEN_CTRL_ID_LAST_LEVEL,
4049     SCREEN_MASK_MAIN,
4050     "last level"
4051   },
4052   {
4053     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
4054     getScreenMenuButtonPos,
4055     SCREEN_CTRL_ID_NEXT_LEVEL,
4056     SCREEN_MASK_MAIN,
4057     "next level"
4058   },
4059   {
4060     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
4061     getScreenMenuButtonPos,
4062     SCREEN_CTRL_ID_LAST_PLAYER,
4063     SCREEN_MASK_INPUT,
4064     "last player"
4065   },
4066   {
4067     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
4068     getScreenMenuButtonPos,
4069     SCREEN_CTRL_ID_NEXT_PLAYER,
4070     SCREEN_MASK_INPUT,
4071     "next player"
4072   },
4073 };
4074
4075 static struct
4076 {
4077   int gfx_unpressed, gfx_pressed;
4078   int x, y;
4079   int gadget_id;
4080   char *infotext;
4081 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
4082 {
4083   {
4084     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
4085     SC_SCROLL_UP_XPOS, SC_SCROLL_UP_YPOS,
4086     SCREEN_CTRL_ID_SCROLL_UP,
4087     "scroll up"
4088   },
4089   {
4090     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
4091     SC_SCROLL_DOWN_XPOS, SC_SCROLL_DOWN_YPOS,
4092     SCREEN_CTRL_ID_SCROLL_DOWN,
4093     "scroll down"
4094   }
4095 };
4096
4097 static struct
4098 {
4099 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4100   Bitmap **gfx_unpressed, **gfx_pressed;
4101 #else
4102   int gfx_unpressed, gfx_pressed;
4103 #endif
4104   int x, y;
4105   int width, height;
4106   int type;
4107   int gadget_id;
4108   char *infotext;
4109 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
4110 {
4111   {
4112 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4113     &scrollbar_bitmap[0], &scrollbar_bitmap[1],
4114 #else
4115     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
4116 #endif
4117     SC_SCROLL_VERTICAL_XPOS, SC_SCROLL_VERTICAL_YPOS,
4118     SC_SCROLL_VERTICAL_XSIZE, SC_SCROLL_VERTICAL_YSIZE,
4119     GD_TYPE_SCROLLBAR_VERTICAL,
4120     SCREEN_CTRL_ID_SCROLL_VERTICAL,
4121     "scroll level series vertically"
4122   }
4123 };
4124
4125 static void CreateScreenMenubuttons()
4126 {
4127   struct GadgetInfo *gi;
4128   unsigned long event_mask;
4129   int i;
4130
4131   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
4132   {
4133     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4134     int gfx_unpressed, gfx_pressed;
4135     int x, y, width, height;
4136     int gd_x1, gd_x2, gd_y1, gd_y2;
4137     int id = menubutton_info[i].gadget_id;
4138
4139     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
4140
4141     menubutton_info[i].get_gadget_position(&x, &y, id);
4142
4143     width = SC_MENUBUTTON_XSIZE;
4144     height = SC_MENUBUTTON_YSIZE;
4145
4146     gfx_unpressed = menubutton_info[i].gfx_unpressed;
4147     gfx_pressed   = menubutton_info[i].gfx_pressed;
4148     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4149     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4150     gd_x1 = graphic_info[gfx_unpressed].src_x;
4151     gd_y1 = graphic_info[gfx_unpressed].src_y;
4152     gd_x2 = graphic_info[gfx_pressed].src_x;
4153     gd_y2 = graphic_info[gfx_pressed].src_y;
4154
4155     gi = CreateGadget(GDI_CUSTOM_ID, id,
4156                       GDI_CUSTOM_TYPE_ID, i,
4157                       GDI_INFO_TEXT, menubutton_info[i].infotext,
4158                       GDI_X, x,
4159                       GDI_Y, y,
4160                       GDI_WIDTH, width,
4161                       GDI_HEIGHT, height,
4162                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4163                       GDI_STATE, GD_BUTTON_UNPRESSED,
4164                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4165                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4166                       GDI_DIRECT_DRAW, FALSE,
4167                       GDI_EVENT_MASK, event_mask,
4168                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4169                       GDI_END);
4170
4171     if (gi == NULL)
4172       Error(ERR_EXIT, "cannot create gadget");
4173
4174     screen_gadget[id] = gi;
4175   }
4176 }
4177
4178 static void CreateScreenScrollbuttons()
4179 {
4180   struct GadgetInfo *gi;
4181   unsigned long event_mask;
4182   int i;
4183
4184   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
4185   {
4186     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4187     int gfx_unpressed, gfx_pressed;
4188     int x, y, width, height;
4189     int gd_x1, gd_x2, gd_y1, gd_y2;
4190     int id = scrollbutton_info[i].gadget_id;
4191
4192     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
4193
4194     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
4195     y = mSY + scrollbutton_info[i].y;
4196     width = SC_SCROLLBUTTON_XSIZE;
4197     height = SC_SCROLLBUTTON_YSIZE;
4198
4199     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
4200       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
4201                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
4202
4203     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
4204     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
4205     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4206     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4207     gd_x1 = graphic_info[gfx_unpressed].src_x;
4208     gd_y1 = graphic_info[gfx_unpressed].src_y;
4209     gd_x2 = graphic_info[gfx_pressed].src_x;
4210     gd_y2 = graphic_info[gfx_pressed].src_y;
4211
4212     gi = CreateGadget(GDI_CUSTOM_ID, id,
4213                       GDI_CUSTOM_TYPE_ID, i,
4214                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
4215                       GDI_X, x,
4216                       GDI_Y, y,
4217                       GDI_WIDTH, width,
4218                       GDI_HEIGHT, height,
4219                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4220                       GDI_STATE, GD_BUTTON_UNPRESSED,
4221                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4222                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4223                       GDI_DIRECT_DRAW, FALSE,
4224                       GDI_EVENT_MASK, event_mask,
4225                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4226                       GDI_END);
4227
4228     if (gi == NULL)
4229       Error(ERR_EXIT, "cannot create gadget");
4230
4231     screen_gadget[id] = gi;
4232   }
4233 }
4234
4235 static void CreateScreenScrollbars()
4236 {
4237   int i;
4238
4239   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
4240   {
4241     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4242 #if !defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4243     int gfx_unpressed, gfx_pressed;
4244 #endif
4245     int x, y, width, height;
4246     int gd_x1, gd_x2, gd_y1, gd_y2;
4247     struct GadgetInfo *gi;
4248     int items_max, items_visible, item_position;
4249     unsigned long event_mask;
4250     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
4251     int id = scrollbar_info[i].gadget_id;
4252
4253     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
4254
4255     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
4256     y = mSY + scrollbar_info[i].y;
4257     width  = scrollbar_info[i].width;
4258     height = scrollbar_info[i].height;
4259
4260     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
4261       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
4262
4263     items_max = num_page_entries;
4264     items_visible = num_page_entries;
4265     item_position = 0;
4266
4267 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4268     gd_bitmap_unpressed = *scrollbar_info[i].gfx_unpressed;
4269     gd_bitmap_pressed   = *scrollbar_info[i].gfx_pressed;
4270     gd_x1 = 0;
4271     gd_y1 = 0;
4272     gd_x2 = 0;
4273     gd_y2 = 0;
4274 #else
4275     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
4276     gfx_pressed   = scrollbar_info[i].gfx_pressed;
4277     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4278     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4279     gd_x1 = graphic_info[gfx_unpressed].src_x;
4280     gd_y1 = graphic_info[gfx_unpressed].src_y;
4281     gd_x2 = graphic_info[gfx_pressed].src_x;
4282     gd_y2 = graphic_info[gfx_pressed].src_y;
4283 #endif
4284
4285     gi = CreateGadget(GDI_CUSTOM_ID, id,
4286                       GDI_CUSTOM_TYPE_ID, i,
4287                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
4288                       GDI_X, x,
4289                       GDI_Y, y,
4290                       GDI_WIDTH, width,
4291                       GDI_HEIGHT, height,
4292                       GDI_TYPE, scrollbar_info[i].type,
4293                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
4294                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
4295                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
4296                       GDI_WHEEL_AREA_X, 0,
4297                       GDI_WHEEL_AREA_Y, 0,
4298                       GDI_WHEEL_AREA_WIDTH, WIN_XSIZE,
4299                       GDI_WHEEL_AREA_HEIGHT, WIN_YSIZE,
4300                       GDI_STATE, GD_BUTTON_UNPRESSED,
4301                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4302                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4303                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
4304                       GDI_DIRECT_DRAW, FALSE,
4305                       GDI_EVENT_MASK, event_mask,
4306                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4307                       GDI_END);
4308
4309     if (gi == NULL)
4310       Error(ERR_EXIT, "cannot create gadget");
4311
4312     screen_gadget[id] = gi;
4313   }
4314 }
4315
4316 void CreateScreenGadgets()
4317 {
4318   int last_game_status = game_status;   /* save current game status */
4319
4320 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4321   int i;
4322
4323   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
4324   {
4325     scrollbar_bitmap[i] = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
4326
4327     /* copy pointers to clip mask and GC */
4328     scrollbar_bitmap[i]->clip_mask =
4329       graphic_info[IMG_MENU_SCROLLBAR + i].clip_mask;
4330     scrollbar_bitmap[i]->stored_clip_gc =
4331       graphic_info[IMG_MENU_SCROLLBAR + i].clip_gc;
4332
4333     BlitBitmap(graphic_info[IMG_MENU_SCROLLBAR + i].bitmap,
4334                scrollbar_bitmap[i],
4335                graphic_info[IMG_MENU_SCROLLBAR + i].src_x,
4336                graphic_info[IMG_MENU_SCROLLBAR + i].src_y,
4337                TILEX, TILEY, 0, 0);
4338   }
4339 #endif
4340
4341   CreateScreenMenubuttons();
4342
4343   /* force LEVELS draw offset for scrollbar / scrollbutton gadgets */
4344   game_status = GAME_MODE_LEVELS;
4345
4346   CreateScreenScrollbuttons();
4347   CreateScreenScrollbars();
4348
4349   game_status = last_game_status;       /* restore current game status */
4350 }
4351
4352 void FreeScreenGadgets()
4353 {
4354   int i;
4355
4356 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4357   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
4358   {
4359     /* prevent freeing clip mask and GC twice */
4360     scrollbar_bitmap[i]->clip_mask = None;
4361     scrollbar_bitmap[i]->stored_clip_gc = None;
4362
4363     FreeBitmap(scrollbar_bitmap[i]);
4364   }
4365 #endif
4366
4367   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
4368     FreeGadget(screen_gadget[i]);
4369 }
4370
4371 void MapScreenMenuGadgets(int screen_mask)
4372 {
4373   int i;
4374
4375   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
4376     if (screen_mask & menubutton_info[i].screen_mask)
4377       MapGadget(screen_gadget[menubutton_info[i].gadget_id]);
4378 }
4379
4380 void MapScreenTreeGadgets(TreeInfo *ti)
4381 {
4382   int num_entries = numTreeInfoInGroup(ti);
4383   int i;
4384
4385   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
4386     return;
4387
4388   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
4389     MapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
4390
4391   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
4392     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
4393 }
4394
4395 static void HandleScreenGadgets(struct GadgetInfo *gi)
4396 {
4397   int id = gi->custom_id;
4398   int button = gi->event.button;
4399   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4400
4401 #if 0
4402   if (game_status != GAME_MODE_LEVELS && game_status != GAME_MODE_SETUP)
4403     return;
4404 #endif
4405
4406   switch (id)
4407   {
4408     case SCREEN_CTRL_ID_LAST_LEVEL:
4409       HandleMainMenu_SelectLevel(step, -1);
4410       break;
4411
4412     case SCREEN_CTRL_ID_NEXT_LEVEL:
4413       HandleMainMenu_SelectLevel(step, +1);
4414       break;
4415
4416     case SCREEN_CTRL_ID_LAST_PLAYER:
4417       HandleSetupScreen_Input_Player(step, -1);
4418       break;
4419
4420     case SCREEN_CTRL_ID_NEXT_PLAYER:
4421       HandleSetupScreen_Input_Player(step, +1);
4422       break;
4423
4424     case SCREEN_CTRL_ID_SCROLL_UP:
4425       if (game_status == GAME_MODE_LEVELS)
4426         HandleChooseLevel(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
4427       else if (game_status == GAME_MODE_SETUP)
4428         HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
4429       break;
4430
4431     case SCREEN_CTRL_ID_SCROLL_DOWN:
4432       if (game_status == GAME_MODE_LEVELS)
4433         HandleChooseLevel(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4434       else if (game_status == GAME_MODE_SETUP)
4435         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4436       break;
4437
4438     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
4439       if (game_status == GAME_MODE_LEVELS)
4440         HandleChooseLevel(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4441       else if (game_status == GAME_MODE_SETUP)
4442         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4443       break;
4444
4445     default:
4446       break;
4447   }
4448 }