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