rnd-20060804-1-src
[rocksndiamonds.git] / src / screens.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * screens.c                                                *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "screens.h"
17 #include "events.h"
18 #include "game.h"
19 #include "tools.h"
20 #include "editor.h"
21 #include "files.h"
22 #include "tape.h"
23 #include "cartoons.h"
24 #include "network.h"
25 #include "init.h"
26
27 /* screens in the setup menu */
28 #define SETUP_MODE_MAIN                 0
29 #define SETUP_MODE_GAME                 1
30 #define SETUP_MODE_EDITOR               2
31 #define SETUP_MODE_INPUT                3
32 #define SETUP_MODE_SHORTCUT_1           4
33 #define SETUP_MODE_SHORTCUT_2           5
34 #define SETUP_MODE_GRAPHICS             6
35 #define SETUP_MODE_CHOOSE_SCREEN_MODE   7
36 #define SETUP_MODE_SOUND                8
37 #define SETUP_MODE_ARTWORK              9
38 #define SETUP_MODE_CHOOSE_GRAPHICS      10
39 #define SETUP_MODE_CHOOSE_SOUNDS        11
40 #define SETUP_MODE_CHOOSE_MUSIC         12
41
42 #define MAX_SETUP_MODES                 13
43
44 /* for input setup functions */
45 #define SETUPINPUT_SCREEN_POS_START     0
46 #define SETUPINPUT_SCREEN_POS_END       (SCR_FIELDY - 4)
47 #define SETUPINPUT_SCREEN_POS_EMPTY1    (SETUPINPUT_SCREEN_POS_START + 3)
48 #define SETUPINPUT_SCREEN_POS_EMPTY2    (SETUPINPUT_SCREEN_POS_END - 1)
49
50 /* screens on the info screen */
51 #define INFO_MODE_MAIN                  0
52 #define INFO_MODE_TITLE                 1
53 #define INFO_MODE_ELEMENTS              2
54 #define INFO_MODE_MUSIC                 3
55 #define INFO_MODE_CREDITS               4
56 #define INFO_MODE_PROGRAM               5
57 #define INFO_MODE_LEVELSET              6
58
59 #define MAX_INFO_MODES                  7
60
61 /* for various menu stuff  */
62 #define MENU_SCREEN_START_XPOS          1
63 #define MENU_SCREEN_START_YPOS          2
64 #define MENU_SCREEN_VALUE_XPOS          14
65 #define MENU_SCREEN_MAX_XPOS            (SCR_FIELDX - 1)
66 #define MENU_TITLE1_YPOS                8
67 #define MENU_TITLE2_YPOS                46
68 #define MAX_INFO_ELEMENTS_ON_SCREEN     10
69 #define MAX_MENU_ENTRIES_ON_SCREEN      (SCR_FIELDY - MENU_SCREEN_START_YPOS)
70 #define MAX_MENU_TEXT_LENGTH_BIG        (MENU_SCREEN_VALUE_XPOS -       \
71                                          MENU_SCREEN_START_XPOS)
72 #define MAX_MENU_TEXT_LENGTH_MEDIUM     (MAX_MENU_TEXT_LENGTH_BIG * 2)
73
74 /* buttons and scrollbars identifiers */
75 #define SCREEN_CTRL_ID_LAST_LEVEL       0
76 #define SCREEN_CTRL_ID_NEXT_LEVEL       1
77 #define SCREEN_CTRL_ID_LAST_PLAYER      2
78 #define SCREEN_CTRL_ID_NEXT_PLAYER      3
79 #define SCREEN_CTRL_ID_SCROLL_UP        4
80 #define SCREEN_CTRL_ID_SCROLL_DOWN      5
81 #define SCREEN_CTRL_ID_SCROLL_VERTICAL  6
82
83 #define NUM_SCREEN_GADGETS              7
84
85 #define NUM_SCREEN_MENUBUTTONS          4
86 #define NUM_SCREEN_SCROLLBUTTONS        2
87 #define NUM_SCREEN_SCROLLBARS           1
88
89 #define SCREEN_MASK_MAIN                (1 << 0)
90 #define SCREEN_MASK_INPUT               (1 << 1)
91
92 /* graphic position and size values for buttons and scrollbars */
93 #define SC_MENUBUTTON_XSIZE             TILEX
94 #define SC_MENUBUTTON_YSIZE             TILEY
95
96 #define SC_SCROLLBUTTON_XSIZE           TILEX
97 #define SC_SCROLLBUTTON_YSIZE           TILEY
98
99 #define SC_SCROLLBAR_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
100
101 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
102 #define SC_SCROLL_VERTICAL_YSIZE        ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \
103                                          SC_SCROLLBUTTON_YSIZE)
104
105 #define SC_SCROLL_UP_XPOS               SC_SCROLLBAR_XPOS
106 #define SC_SCROLL_UP_YPOS               (2 * SC_SCROLLBUTTON_YSIZE)
107
108 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLLBAR_XPOS
109 #define SC_SCROLL_VERTICAL_YPOS         (SC_SCROLL_UP_YPOS + \
110                                          SC_SCROLLBUTTON_YSIZE)
111
112 #define SC_SCROLL_DOWN_XPOS             SC_SCROLLBAR_XPOS
113 #define SC_SCROLL_DOWN_YPOS             (SC_SCROLL_VERTICAL_YPOS + \
114                                          SC_SCROLL_VERTICAL_YSIZE)
115
116 #define SC_BORDER_SIZE                  14
117
118
119 /* forward declarations of internal functions */
120 static void HandleScreenGadgets(struct GadgetInfo *);
121 static void HandleSetupScreen_Generic(int, int, int, int, int);
122 static void HandleSetupScreen_Input(int, int, int, int, int);
123 static void CustomizeKeyboard(int);
124 static void CalibrateJoystick(int);
125 static void execSetupGraphics(void);
126 static void execSetupArtwork(void);
127 static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
128
129 static void DrawChooseLevel(void);
130 static void DrawInfoScreen(void);
131 static void DrawSetupScreen(void);
132
133 static void DrawInfoScreenExt(int);
134 static void DrawInfoScreen_NotAvailable(char *, char *);
135 static void DrawInfoScreen_HelpAnim(int, int, boolean);
136 static void DrawInfoScreen_HelpText(int, int, int, int);
137 static void HandleInfoScreen_Main(int, int, int, int, int);
138 static void HandleInfoScreen_TitleScreen(int);
139 static void HandleInfoScreen_Elements(int);
140 static void HandleInfoScreen_Music(int);
141 static void HandleInfoScreen_Credits(int);
142 static void HandleInfoScreen_Program(int);
143
144 static void MapScreenMenuGadgets(int);
145 static void MapScreenTreeGadgets(TreeInfo *);
146
147 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
148 static int setup_mode = SETUP_MODE_MAIN;
149 static int info_mode = INFO_MODE_MAIN;
150
151 static TreeInfo *screen_modes = NULL;
152 static TreeInfo *screen_mode_current = NULL;
153
154 #define DRAW_OFFSET_MODE(x)     (x >= GAME_MODE_MAIN &&                 \
155                                  x <= GAME_MODE_SETUP ? x :             \
156                                  x == GAME_MODE_PSEUDO_TYPENAME ?       \
157                                  GAME_MODE_MAIN : GAME_MODE_DEFAULT)
158
159 #define mSX (SX + menu.draw_xoffset[DRAW_OFFSET_MODE(game_status)])
160 #define mSY (SY + menu.draw_yoffset[DRAW_OFFSET_MODE(game_status)])
161
162 #define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ?   \
163                                     menu.list_size[game_status] :       \
164                                     MAX_MENU_ENTRIES_ON_SCREEN)
165
166 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
167 #define NUM_SCROLLBAR_BITMAPS           2
168 static Bitmap *scrollbar_bitmap[NUM_SCROLLBAR_BITMAPS];
169 #endif
170
171
172 static void drawCursorExt(int xpos, int ypos, int color, int g)
173 {
174   static int cursor_array[SCR_FIELDY];
175
176   if (xpos == 0)
177   {
178     if (g != 0)
179       cursor_array[ypos] = g;
180     else
181       g = cursor_array[ypos];
182   }
183
184   if (color == FC_RED)
185     g = (g == IMG_MENU_BUTTON_LEFT  ? IMG_MENU_BUTTON_LEFT_ACTIVE  :
186          g == IMG_MENU_BUTTON_RIGHT ? IMG_MENU_BUTTON_RIGHT_ACTIVE :
187          g == IMG_MENU_BUTTON_LEAVE_MENU ? IMG_MENU_BUTTON_LEAVE_MENU_ACTIVE :
188          g == IMG_MENU_BUTTON_ENTER_MENU ? IMG_MENU_BUTTON_ENTER_MENU_ACTIVE :
189          g == IMG_MENU_BUTTON_LAST_LEVEL ? IMG_MENU_BUTTON_LAST_LEVEL_ACTIVE :
190          g == IMG_MENU_BUTTON_NEXT_LEVEL ? IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE :
191          IMG_MENU_BUTTON_ACTIVE);
192
193   ypos += MENU_SCREEN_START_YPOS;
194
195   DrawBackground(mSX + xpos * TILEX, mSY + ypos * TILEY, TILEX, TILEY);
196   DrawGraphicThruMaskExt(drawto, mSX + xpos * TILEX, mSY + ypos * TILEY, g, 0);
197 }
198
199 static void initCursor(int ypos, int graphic)
200 {
201   drawCursorExt(0, ypos, FC_BLUE, graphic);
202 }
203
204 static void drawCursor(int ypos, int color)
205 {
206   drawCursorExt(0, ypos, color, 0);
207 }
208
209 static void drawCursorXY(int xpos, int ypos, int graphic)
210 {
211   drawCursorExt(xpos, ypos, -1, graphic);
212 }
213
214 static void drawChooseTreeCursor(int ypos, int color)
215 {
216   int last_game_status = game_status;   /* save current game status */
217
218   /* force LEVELS draw offset on artwork setup screen */
219   game_status = GAME_MODE_LEVELS;
220
221   drawCursorExt(0, ypos, color, 0);
222
223   game_status = last_game_status;       /* restore current game status */
224 }
225
226 void DrawHeadline()
227 {
228   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, PROGRAM_TITLE_STRING);
229   DrawTextSCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, PROGRAM_COPYRIGHT_STRING);
230 }
231
232 static void ToggleFullscreenIfNeeded()
233 {
234   if (setup.fullscreen != video.fullscreen_enabled ||
235       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                     "info@artsoft.org");
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
1804   DrawTextSCentered(ystart + 7 * ystep, FONT_TEXT_2,
1805                     "If you have created new levels,");
1806   DrawTextSCentered(ystart + 8 * ystep, FONT_TEXT_2,
1807                     "send them to me to include them!");
1808   DrawTextSCentered(ystart + 9 * ystep, FONT_TEXT_2,
1809                     ":-)");
1810
1811   DrawTextSCentered(ybottom, FONT_TEXT_4,
1812                     "Press any key or button for info menu");
1813 }
1814
1815 void HandleInfoScreen_Program(int button)
1816 {
1817   int button_released = !button;
1818
1819   if (button == MB_MENU_LEAVE)
1820   {
1821     info_mode = INFO_MODE_MAIN;
1822     DrawInfoScreen();
1823
1824     return;
1825   }
1826
1827   if (button_released)
1828   {
1829     FadeSoundsAndMusic();
1830
1831     info_mode = INFO_MODE_MAIN;
1832     DrawInfoScreen();
1833   }
1834   else
1835   {
1836     PlayMenuSoundIfLoop();
1837   }
1838 }
1839
1840 void DrawInfoScreen_LevelSet()
1841 {
1842   int ystart = 150;
1843   int ybottom = SYSIZE - 20;
1844   char *filename = getLevelSetInfoFilename();
1845   int font_nr = FONT_LEVEL_NUMBER;
1846   int font_width = getFontWidth(font_nr);
1847   int font_height = getFontHeight(font_nr);
1848   int pad_x = 32;
1849   int pad_y = ystart;
1850   int sx = SX + pad_x;
1851   int sy = SY + pad_y;
1852   int max_chars_per_line = (SXSIZE - 2 * pad_x) / font_width;
1853   int max_lines_per_screen = (SYSIZE - pad_y) / font_height - 1;
1854
1855   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_LEVELSET);
1856
1857   ClearWindow();
1858   DrawHeadline();
1859
1860   DrawTextSCentered(100, FONT_TEXT_1, "Level Set Information:");
1861
1862   DrawTextSCentered(ybottom, FONT_TEXT_4,
1863                     "Press any key or button for info menu");
1864
1865   if (filename != NULL)
1866     DrawTextFromFile(sx, sy, filename, font_nr, max_chars_per_line,
1867                      max_lines_per_screen);
1868   else
1869     DrawTextSCentered(ystart, FONT_TEXT_2,
1870                       "No information for this level set.");
1871 }
1872
1873 void HandleInfoScreen_LevelSet(int button)
1874 {
1875   int button_released = !button;
1876
1877   if (button == MB_MENU_LEAVE)
1878   {
1879     info_mode = INFO_MODE_MAIN;
1880     DrawInfoScreen();
1881
1882     return;
1883   }
1884
1885   if (button_released)
1886   {
1887     FadeSoundsAndMusic();
1888
1889     info_mode = INFO_MODE_MAIN;
1890     DrawInfoScreen();
1891   }
1892   else
1893   {
1894     PlayMenuSoundIfLoop();
1895   }
1896 }
1897
1898 static void DrawInfoScreenExt(int fade_delay)
1899 {
1900   SetMainBackgroundImage(IMG_BACKGROUND_INFO);
1901
1902   if (info_mode == INFO_MODE_TITLE)
1903     DrawInfoScreen_TitleScreen();
1904   else if (info_mode == INFO_MODE_ELEMENTS)
1905     DrawInfoScreen_Elements();
1906   else if (info_mode == INFO_MODE_MUSIC)
1907     DrawInfoScreen_Music();
1908   else if (info_mode == INFO_MODE_CREDITS)
1909     DrawInfoScreen_Credits();
1910   else if (info_mode == INFO_MODE_PROGRAM)
1911     DrawInfoScreen_Program();
1912   else if (info_mode == INFO_MODE_LEVELSET)
1913     DrawInfoScreen_LevelSet();
1914   else
1915     DrawInfoScreen_Main(fade_delay);
1916
1917   if (info_mode != INFO_MODE_MAIN &&
1918       info_mode != INFO_MODE_TITLE &&
1919       info_mode != INFO_MODE_MUSIC)
1920   {
1921     PlayMenuSound();
1922     PlayMenuMusic();
1923   }
1924 }
1925
1926 void DrawInfoScreen()
1927 {
1928   DrawInfoScreenExt(0);
1929 }
1930
1931 void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
1932 {
1933   if (info_mode == INFO_MODE_TITLE)
1934     HandleInfoScreen_TitleScreen(button);
1935   else if (info_mode == INFO_MODE_ELEMENTS)
1936     HandleInfoScreen_Elements(button);
1937   else if (info_mode == INFO_MODE_MUSIC)
1938     HandleInfoScreen_Music(button);
1939   else if (info_mode == INFO_MODE_CREDITS)
1940     HandleInfoScreen_Credits(button);
1941   else if (info_mode == INFO_MODE_PROGRAM)
1942     HandleInfoScreen_Program(button);
1943   else if (info_mode == INFO_MODE_LEVELSET)
1944     HandleInfoScreen_LevelSet(button);
1945   else
1946     HandleInfoScreen_Main(mx, my, dx, dy, button);
1947
1948   DoAnimation();
1949 }
1950
1951
1952 /* ========================================================================= */
1953 /* type name functions                                                       */
1954 /* ========================================================================= */
1955
1956 void HandleTypeName(int newxpos, Key key)
1957 {
1958   static int xpos = 0, ypos = 2;
1959   int font_width = getFontWidth(FONT_INPUT_1_ACTIVE);
1960   int name_width = getFontWidth(FONT_MENU_1) * strlen("Name:");
1961   int startx = mSX + 32 + name_width;
1962   int starty = mSY + ypos * 32;
1963
1964   if (newxpos)
1965   {
1966     xpos = newxpos;
1967
1968     DrawText(startx, starty, setup.player_name, FONT_INPUT_1_ACTIVE);
1969     DrawText(startx + xpos * font_width, starty, "_", FONT_INPUT_1_ACTIVE);
1970
1971     return;
1972   }
1973
1974   if (((key >= KSYM_A && key <= KSYM_Z) ||
1975        (key >= KSYM_a && key <= KSYM_z)) && 
1976       xpos < MAX_PLAYER_NAME_LEN)
1977   {
1978     char ascii;
1979
1980     if (key >= KSYM_A && key <= KSYM_Z)
1981       ascii = 'A' + (char)(key - KSYM_A);
1982     else
1983       ascii = 'a' + (char)(key - KSYM_a);
1984
1985     setup.player_name[xpos] = ascii;
1986     setup.player_name[xpos + 1] = 0;
1987     xpos++;
1988
1989     DrawText(startx, starty, setup.player_name, FONT_INPUT_1_ACTIVE);
1990     DrawText(startx + xpos * font_width, starty, "_", FONT_INPUT_1_ACTIVE);
1991   }
1992   else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
1993   {
1994     xpos--;
1995     setup.player_name[xpos] = 0;
1996
1997     DrawText(startx + xpos * font_width, starty, "_ ", FONT_INPUT_1_ACTIVE);
1998   }
1999   else if (key == KSYM_Return && xpos > 0)
2000   {
2001     DrawText(startx, starty, setup.player_name, FONT_INPUT_1);
2002     DrawText(startx + xpos * font_width, starty, " ", FONT_INPUT_1_ACTIVE);
2003
2004     SaveSetup();
2005     game_status = GAME_MODE_MAIN;
2006   }
2007 }
2008
2009
2010 /* ========================================================================= */
2011 /* tree menu functions                                                       */
2012 /* ========================================================================= */
2013
2014 static void DrawChooseTree(TreeInfo **ti_ptr)
2015 {
2016   UnmapAllGadgets();
2017
2018   FreeScreenGadgets();
2019   CreateScreenGadgets();
2020
2021   CloseDoor(DOOR_CLOSE_2);
2022
2023   ClearWindow();
2024
2025   HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
2026   MapScreenTreeGadgets(*ti_ptr);
2027
2028   FadeToFront();
2029   InitAnimation();
2030 }
2031
2032 static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti)
2033 {
2034   struct GadgetInfo *gi = screen_gadget[id];
2035   int items_max, items_visible, item_position;
2036
2037   items_max = numTreeInfoInGroup(ti);
2038   items_visible = NUM_MENU_ENTRIES_ON_SCREEN;
2039   item_position = first_entry;
2040
2041   if (item_position > items_max - items_visible)
2042     item_position = items_max - items_visible;
2043
2044   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
2045                GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
2046                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
2047 }
2048
2049 static void drawChooseTreeList(int first_entry, int num_page_entries,
2050                                TreeInfo *ti)
2051 {
2052   int i;
2053   char *title_string = NULL;
2054 #if 0
2055   int xoffset_sets = 16;
2056 #endif
2057   int yoffset_sets = MENU_TITLE1_YPOS;
2058 #if 0
2059   int xoffset_setup = 16;
2060 #endif
2061   int yoffset_setup = 16;
2062 #if 1
2063 #if 0
2064   int xoffset = (ti->type == TREE_TYPE_LEVEL_DIR ? xoffset_sets :
2065                  xoffset_setup);
2066 #endif
2067   int yoffset = (ti->type == TREE_TYPE_LEVEL_DIR ? yoffset_sets :
2068                  yoffset_setup);
2069 #else
2070   int xoffset = (ti->type == TREE_TYPE_LEVEL_DIR ? 0 : xoffset_setup);
2071   int yoffset = (ti->type == TREE_TYPE_LEVEL_DIR ? 0 : yoffset_setup);
2072 #endif
2073   int last_game_status = game_status;   /* save current game status */
2074
2075   title_string = ti->infotext;
2076
2077 #if 1
2078   DrawTextSCentered(mSY - SY + yoffset, FONT_TITLE_1, title_string);
2079 #else
2080   DrawText(SX + xoffset, SY + yoffset, title_string, FONT_TITLE_1);
2081 #endif
2082
2083   /* force LEVELS font on artwork setup screen */
2084   game_status = GAME_MODE_LEVELS;
2085
2086   /* clear tree list area, but not title or scrollbar */
2087   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
2088                  SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset,
2089                  MAX_MENU_ENTRIES_ON_SCREEN * 32);
2090
2091   for (i = 0; i < num_page_entries; i++)
2092   {
2093     TreeInfo *node, *node_first;
2094     int entry_pos = first_entry + i;
2095     int xpos = MENU_SCREEN_START_XPOS;
2096     int ypos = MENU_SCREEN_START_YPOS + i;
2097     int startx = mSX + xpos * 32;
2098     int starty = mSY + ypos * 32;
2099     int font_nr = FONT_TEXT_1;
2100     int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
2101     int startx_text = startx + font_xoffset;
2102     int startx_scrollbar = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
2103     int text_size = startx_scrollbar - startx_text;
2104     int max_buffer_len = text_size / getFontWidth(font_nr);
2105     char buffer[max_buffer_len + 1];
2106
2107     node_first = getTreeInfoFirstGroupEntry(ti);
2108     node = getTreeInfoFromPos(node_first, entry_pos);
2109
2110     strncpy(buffer, node->name, max_buffer_len);
2111     buffer[max_buffer_len] = '\0';
2112
2113     DrawText(startx, starty, buffer, font_nr + node->color);
2114
2115     if (node->parent_link)
2116       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
2117     else if (node->level_group)
2118       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
2119     else
2120       initCursor(i, IMG_MENU_BUTTON);
2121   }
2122
2123   game_status = last_game_status;       /* restore current game status */
2124
2125   redraw_mask |= REDRAW_FIELD;
2126 }
2127
2128 static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
2129 {
2130   TreeInfo *node, *node_first;
2131   int x, last_redraw_mask = redraw_mask;
2132 #if 1
2133   int ypos = MENU_TITLE2_YPOS;
2134 #else
2135   int ypos = 40;
2136 #endif
2137
2138   if (ti->type != TREE_TYPE_LEVEL_DIR)
2139     return;
2140
2141   node_first = getTreeInfoFirstGroupEntry(ti);
2142   node = getTreeInfoFromPos(node_first, entry_pos);
2143
2144   DrawBackground(SX, SY + ypos, SXSIZE, getFontHeight(FONT_TITLE_2));
2145
2146   if (node->parent_link)
2147     DrawTextFCentered(ypos, FONT_TITLE_2, "leave group \"%s\"",
2148                       node->class_desc);
2149   else if (node->level_group)
2150     DrawTextFCentered(ypos, FONT_TITLE_2, "enter group \"%s\"",
2151                       node->class_desc);
2152   else if (ti->type == TREE_TYPE_LEVEL_DIR)
2153     DrawTextFCentered(ypos, FONT_TITLE_2, "%3d levels (%s)",
2154                       node->levels, node->class_desc);
2155
2156   /* let BackToFront() redraw only what is needed */
2157   redraw_mask = last_redraw_mask | REDRAW_TILES;
2158   for (x = 0; x < SCR_FIELDX; x++)
2159     MarkTileDirty(x, 1);
2160 }
2161
2162 static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
2163                              TreeInfo **ti_ptr)
2164 {
2165   TreeInfo *ti = *ti_ptr;
2166   int x = 0;
2167   int y = ti->cl_cursor;
2168   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2169   int num_entries = numTreeInfoInGroup(ti);
2170   int num_page_entries;
2171   int last_game_status = game_status;   /* save current game status */
2172   boolean position_set_by_scrollbar = (dx == 999);
2173
2174   /* force LEVELS draw offset on choose level and artwork setup screen */
2175   game_status = GAME_MODE_LEVELS;
2176
2177   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
2178     num_page_entries = num_entries;
2179   else
2180     num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
2181
2182   game_status = last_game_status;       /* restore current game status */
2183
2184   if (button == MB_MENU_INITIALIZE)
2185   {
2186     int num_entries = numTreeInfoInGroup(ti);
2187     int entry_pos = posTreeInfo(ti);
2188
2189     if (ti->cl_first == -1)
2190     {
2191       /* only on initialization */
2192       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
2193       ti->cl_cursor = entry_pos - ti->cl_first;
2194     }
2195     else if (ti->cl_cursor >= num_page_entries ||
2196              (num_entries > num_page_entries &&
2197               num_entries - ti->cl_first < num_page_entries))
2198     {
2199       /* only after change of list size (by custom graphic configuration) */
2200       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
2201       ti->cl_cursor = entry_pos - ti->cl_first;
2202     }
2203
2204     if (position_set_by_scrollbar)
2205       ti->cl_first = dy;
2206     else
2207       AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2208                                 ti->cl_first, ti);
2209
2210     drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2211     drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2212     drawChooseTreeCursor(ti->cl_cursor, FC_RED);
2213
2214     return;
2215   }
2216   else if (button == MB_MENU_LEAVE)
2217   {
2218     if (ti->node_parent)
2219     {
2220       *ti_ptr = ti->node_parent;
2221       DrawChooseTree(ti_ptr);
2222     }
2223     else if (game_status == GAME_MODE_SETUP)
2224     {
2225       if (game_status == GAME_MODE_SETUP)
2226       {
2227         if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
2228           execSetupGraphics();
2229         else
2230           execSetupArtwork();
2231       }
2232     }
2233     else
2234     {
2235       game_status = GAME_MODE_MAIN;
2236       DrawMainMenu();
2237     }
2238
2239     return;
2240   }
2241
2242   if (mx || my)         /* mouse input */
2243   {
2244     int last_game_status = game_status; /* save current game status */
2245
2246     /* force LEVELS draw offset on artwork setup screen */
2247     game_status = GAME_MODE_LEVELS;
2248
2249     x = (mx - mSX) / 32;
2250     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
2251
2252     game_status = last_game_status;     /* restore current game status */
2253   }
2254   else if (dx || dy)    /* keyboard or scrollbar/scrollbutton input */
2255   {
2256     /* move cursor instead of scrolling when already at start/end of list */
2257     if (dy == -1 * SCROLL_LINE && ti->cl_first == 0)
2258       dy = -1;
2259     else if (dy == +1 * SCROLL_LINE &&
2260              ti->cl_first + num_page_entries == num_entries)
2261       dy = 1;
2262
2263     /* handle scrolling screen one line or page */
2264     if (ti->cl_cursor + dy < 0 ||
2265         ti->cl_cursor + dy > num_page_entries - 1)
2266     {
2267       if (ABS(dy) == SCROLL_PAGE)
2268         step = num_page_entries - 1;
2269
2270       if (dy < 0 && ti->cl_first > 0)
2271       {
2272         /* scroll page/line up */
2273
2274         ti->cl_first -= step;
2275         if (ti->cl_first < 0)
2276           ti->cl_first = 0;
2277
2278         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2279         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2280         drawChooseTreeCursor(ti->cl_cursor, FC_RED);
2281         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2282                                   ti->cl_first, ti);
2283       }
2284       else if (dy > 0 && ti->cl_first + num_page_entries < num_entries)
2285       {
2286         /* scroll page/line down */
2287
2288         ti->cl_first += step;
2289         if (ti->cl_first + num_page_entries > num_entries)
2290           ti->cl_first = MAX(0, num_entries - num_page_entries);
2291
2292         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2293         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2294         drawChooseTreeCursor(ti->cl_cursor, FC_RED);
2295         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2296                                   ti->cl_first, ti);
2297       }
2298
2299       return;
2300     }
2301
2302     /* handle moving cursor one line */
2303     y = ti->cl_cursor + dy;
2304   }
2305
2306   if (dx == 1)
2307   {
2308     TreeInfo *node_first, *node_cursor;
2309     int entry_pos = ti->cl_first + y;
2310
2311     node_first = getTreeInfoFirstGroupEntry(ti);
2312     node_cursor = getTreeInfoFromPos(node_first, entry_pos);
2313
2314     if (node_cursor->node_group)
2315     {
2316       node_cursor->cl_first = ti->cl_first;
2317       node_cursor->cl_cursor = ti->cl_cursor;
2318       *ti_ptr = node_cursor->node_group;
2319       DrawChooseTree(ti_ptr);
2320
2321       return;
2322     }
2323   }
2324   else if (dx == -1 && ti->node_parent)
2325   {
2326     *ti_ptr = ti->node_parent;
2327     DrawChooseTree(ti_ptr);
2328
2329     return;
2330   }
2331
2332   if (!anyScrollbarGadgetActive() &&
2333       IN_VIS_FIELD(x, y) &&
2334       mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
2335       y >= 0 && y < num_page_entries)
2336   {
2337     if (button)
2338     {
2339       if (y != ti->cl_cursor)
2340       {
2341         drawChooseTreeCursor(y, FC_RED);
2342         drawChooseTreeCursor(ti->cl_cursor, FC_BLUE);
2343         drawChooseTreeInfo(ti->cl_first + y, ti);
2344         ti->cl_cursor = y;
2345       }
2346     }
2347     else
2348     {
2349       TreeInfo *node_first, *node_cursor;
2350       int entry_pos = ti->cl_first + y;
2351
2352       node_first = getTreeInfoFirstGroupEntry(ti);
2353       node_cursor = getTreeInfoFromPos(node_first, entry_pos);
2354
2355       if (node_cursor->node_group)
2356       {
2357         node_cursor->cl_first = ti->cl_first;
2358         node_cursor->cl_cursor = ti->cl_cursor;
2359         *ti_ptr = node_cursor->node_group;
2360         DrawChooseTree(ti_ptr);
2361       }
2362       else if (node_cursor->parent_link)
2363       {
2364         *ti_ptr = node_cursor->node_parent;
2365         DrawChooseTree(ti_ptr);
2366       }
2367       else
2368       {
2369         node_cursor->cl_first = ti->cl_first;
2370         node_cursor->cl_cursor = ti->cl_cursor;
2371         *ti_ptr = node_cursor;
2372
2373         if (ti->type == TREE_TYPE_LEVEL_DIR)
2374         {
2375           LoadLevelSetup_SeriesInfo();
2376
2377           SaveLevelSetup_LastSeries();
2378           SaveLevelSetup_SeriesInfo();
2379           TapeErase();
2380         }
2381
2382         if (game_status == GAME_MODE_SETUP)
2383         {
2384           if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
2385             execSetupGraphics();
2386           else
2387             execSetupArtwork();
2388         }
2389         else
2390         {
2391           game_status = GAME_MODE_MAIN;
2392           DrawMainMenu();
2393         }
2394       }
2395     }
2396   }
2397 }
2398
2399 void DrawChooseLevel()
2400 {
2401   SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
2402
2403   DrawChooseTree(&leveldir_current);
2404
2405   PlayMenuSound();
2406   PlayMenuMusic();
2407 }
2408
2409 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
2410 {
2411   HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
2412
2413   DoAnimation();
2414 }
2415
2416 void DrawHallOfFame(int highlight_position)
2417 {
2418   UnmapAllGadgets();
2419   FadeSoundsAndMusic();
2420   CloseDoor(DOOR_CLOSE_2);
2421
2422   if (highlight_position < 0) 
2423     LoadScore(level_nr);
2424
2425   FadeToFront();
2426   InitAnimation();
2427
2428   PlayMenuSound();
2429   PlayMenuMusic();
2430
2431   HandleHallOfFame(highlight_position, 0, 0, 0, MB_MENU_INITIALIZE);
2432 }
2433
2434 static void drawHallOfFameList(int first_entry, int highlight_position)
2435 {
2436   int i;
2437
2438   SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
2439   ClearWindow();
2440
2441 #if 1
2442   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame");
2443   DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
2444                     "HighScores of Level %d", level_nr);
2445 #else
2446   DrawText(mSX + 80, mSY + MENU_TITLE1_YPOS, "Hall Of Fame", FONT_TITLE_1);
2447   DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
2448                     "HighScores of Level %d", level_nr);
2449 #endif
2450
2451   for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
2452   {
2453     int entry = first_entry + i;
2454     boolean active = (entry == highlight_position);
2455     int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
2456     int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
2457     int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
2458     int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
2459     int dx1 = 3 * getFontWidth(font_nr1);
2460     int dx2 = dx1 + getFontWidth(font_nr1);
2461     int dx3 = dx2 + 25 * getFontWidth(font_nr3);
2462     int sy = mSY + 64 + i * 32;
2463
2464     DrawText(mSX, sy, int2str(entry + 1, 3), font_nr1);
2465     DrawText(mSX + dx1, sy, ".", font_nr1);
2466     DrawText(mSX + dx2, sy, ".........................", font_nr3);
2467
2468     if (!strEqual(highscore[entry].Name, EMPTY_PLAYER_NAME))
2469       DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
2470
2471     DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
2472   }
2473
2474   redraw_mask |= REDRAW_FIELD;
2475 }
2476
2477 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
2478 {
2479   static int first_entry = 0;
2480   static int highlight_position = 0;
2481   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2482   int button_released = !button;
2483
2484   if (button == MB_MENU_INITIALIZE)
2485   {
2486     first_entry = 0;
2487     highlight_position = mx;
2488     drawHallOfFameList(first_entry, highlight_position);
2489
2490     return;
2491   }
2492
2493   if (ABS(dy) == SCROLL_PAGE)           /* handle scrolling one page */
2494     step = NUM_MENU_ENTRIES_ON_SCREEN - 1;
2495
2496   if (dy < 0)
2497   {
2498     if (first_entry > 0)
2499     {
2500       first_entry -= step;
2501       if (first_entry < 0)
2502         first_entry = 0;
2503
2504       drawHallOfFameList(first_entry, highlight_position);
2505     }
2506   }
2507   else if (dy > 0)
2508   {
2509     if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
2510     {
2511       first_entry += step;
2512       if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
2513         first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
2514
2515       drawHallOfFameList(first_entry, highlight_position);
2516     }
2517   }
2518   else if (button_released)
2519   {
2520     FadeSound(SND_BACKGROUND_SCORES);
2521     game_status = GAME_MODE_MAIN;
2522     DrawMainMenu();
2523   }
2524
2525   if (game_status == GAME_MODE_SCORES)
2526     PlayMenuSoundIfLoop();
2527
2528   DoAnimation();
2529 }
2530
2531
2532 /* ========================================================================= */
2533 /* setup screen functions                                                    */
2534 /* ========================================================================= */
2535
2536 static struct TokenInfo *setup_info;
2537 static int num_setup_info;
2538
2539 static char *screen_mode_text;
2540 static char *graphics_set_name;
2541 static char *sounds_set_name;
2542 static char *music_set_name;
2543
2544 static void execSetupMain()
2545 {
2546   setup_mode = SETUP_MODE_MAIN;
2547   DrawSetupScreen();
2548 }
2549
2550 static void execSetupGame()
2551 {
2552   setup_mode = SETUP_MODE_GAME;
2553   DrawSetupScreen();
2554 }
2555
2556 static void execSetupEditor()
2557 {
2558   setup_mode = SETUP_MODE_EDITOR;
2559   DrawSetupScreen();
2560 }
2561
2562 static void execSetupGraphics()
2563 {
2564   if (video.fullscreen_available && screen_modes == NULL)
2565   {
2566     int i;
2567
2568     for (i = 0; video.fullscreen_modes[i].width != -1; i++)
2569     {
2570       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
2571       char identifier[32], name[32];
2572       int x = video.fullscreen_modes[i].width;
2573       int y = video.fullscreen_modes[i].height;
2574       int xx, yy;
2575
2576       get_aspect_ratio_from_screen_mode(&video.fullscreen_modes[i], &xx, &yy);
2577
2578       ti->node_top = &screen_modes;
2579       ti->sort_priority = x * 10000 + y;
2580
2581       sprintf(identifier, "%dx%d", x, y);
2582       sprintf(name,     "%d x %d [%d:%d]", x, y, xx, yy);
2583
2584       setString(&ti->identifier, identifier);
2585       setString(&ti->name, name);
2586       setString(&ti->name_sorting, name);
2587       setString(&ti->infotext, "Fullscreen Mode");
2588
2589       pushTreeInfo(&screen_modes, ti);
2590     }
2591
2592     /* sort fullscreen modes to start with lowest available screen resolution */
2593     sortTreeInfo(&screen_modes);
2594
2595     /* set current screen mode for fullscreen mode to configured setup value */
2596     screen_mode_current = getTreeInfoFromIdentifier(screen_modes,
2597                                                     setup.fullscreen_mode);
2598
2599     /* if that fails, set current screen mode to reliable default value */
2600     if (screen_mode_current == NULL)
2601       screen_mode_current = getTreeInfoFromIdentifier(screen_modes,
2602                                                       DEFAULT_FULLSCREEN_MODE);
2603
2604     /* if that also fails, set current screen mode to first available mode */
2605     if (screen_mode_current == NULL)
2606       screen_mode_current = screen_modes;
2607
2608     if (screen_mode_current == NULL)
2609       video.fullscreen_available = FALSE;
2610   }
2611
2612   if (video.fullscreen_available)
2613   {
2614     setup.fullscreen_mode = screen_mode_current->identifier;
2615
2616     /* needed for displaying screen mode name instead of identifier */
2617     screen_mode_text = screen_mode_current->name;
2618   }
2619
2620   setup_mode = SETUP_MODE_GRAPHICS;
2621   DrawSetupScreen();
2622 }
2623
2624 static void execSetupChooseScreenMode()
2625 {
2626   if (!video.fullscreen_available)
2627     return;
2628
2629   setup_mode = SETUP_MODE_CHOOSE_SCREEN_MODE;
2630   DrawSetupScreen();
2631 }
2632
2633 static void execSetupSound()
2634 {
2635   setup_mode = SETUP_MODE_SOUND;
2636   DrawSetupScreen();
2637 }
2638
2639 static void execSetupArtwork()
2640 {
2641   setup.graphics_set = artwork.gfx_current->identifier;
2642   setup.sounds_set = artwork.snd_current->identifier;
2643   setup.music_set = artwork.mus_current->identifier;
2644
2645   /* needed if last screen (setup choice) changed graphics, sounds or music */
2646   ReloadCustomArtwork(0);
2647
2648   /* needed for displaying artwork name instead of artwork identifier */
2649   graphics_set_name = artwork.gfx_current->name;
2650   sounds_set_name = artwork.snd_current->name;
2651   music_set_name = artwork.mus_current->name;
2652
2653   setup_mode = SETUP_MODE_ARTWORK;
2654   DrawSetupScreen();
2655 }
2656
2657 static void execSetupChooseGraphics()
2658 {
2659   setup_mode = SETUP_MODE_CHOOSE_GRAPHICS;
2660   DrawSetupScreen();
2661 }
2662
2663 static void execSetupChooseSounds()
2664 {
2665   setup_mode = SETUP_MODE_CHOOSE_SOUNDS;
2666   DrawSetupScreen();
2667 }
2668
2669 static void execSetupChooseMusic()
2670 {
2671   setup_mode = SETUP_MODE_CHOOSE_MUSIC;
2672   DrawSetupScreen();
2673 }
2674
2675 static void execSetupInput()
2676 {
2677   setup_mode = SETUP_MODE_INPUT;
2678   DrawSetupScreen();
2679 }
2680
2681 static void execSetupShortcut1()
2682 {
2683   setup_mode = SETUP_MODE_SHORTCUT_1;
2684   DrawSetupScreen();
2685 }
2686
2687 static void execSetupShortcut2()
2688 {
2689   setup_mode = SETUP_MODE_SHORTCUT_2;
2690   DrawSetupScreen();
2691 }
2692
2693 static void execExitSetup()
2694 {
2695   game_status = GAME_MODE_MAIN;
2696   DrawMainMenu();
2697 }
2698
2699 static void execSaveAndExitSetup()
2700 {
2701   SaveSetup();
2702   execExitSetup();
2703 }
2704
2705 static struct TokenInfo setup_info_main[] =
2706 {
2707   { TYPE_ENTER_MENU,    execSetupGame,          "Game & Menu"           },
2708   { TYPE_ENTER_MENU,    execSetupEditor,        "Editor"                },
2709   { TYPE_ENTER_MENU,    execSetupGraphics,      "Graphics"              },
2710   { TYPE_ENTER_MENU,    execSetupSound,         "Sound & Music"         },
2711   { TYPE_ENTER_MENU,    execSetupArtwork,       "Custom Artwork"        },
2712   { TYPE_ENTER_MENU,    execSetupInput,         "Input Devices"         },
2713   { TYPE_ENTER_MENU,    execSetupShortcut1,     "Key Shortcuts 1"       },
2714   { TYPE_ENTER_MENU,    execSetupShortcut2,     "Key Shortcuts 2"       },
2715   { TYPE_EMPTY,         NULL,                   ""                      },
2716   { TYPE_LEAVE_MENU,    execExitSetup,          "Exit"                  },
2717   { TYPE_LEAVE_MENU,    execSaveAndExitSetup,   "Save and Exit"         },
2718
2719   { 0,                  NULL,                   NULL                    }
2720 };
2721
2722 static struct TokenInfo setup_info_game[] =
2723 {
2724   { TYPE_SWITCH,        &setup.team_mode,       "Team-Mode (Multi-Player):" },
2725   { TYPE_YES_NO,        &setup.input_on_focus,  "Only Move Focussed Player:" },
2726   { TYPE_SWITCH,        &setup.handicap,        "Handicap:"             },
2727   { TYPE_SWITCH,        &setup.skip_levels,     "Skip Unsolved Levels:" },
2728   { TYPE_SWITCH,        &setup.time_limit,      "Time Limit:"           },
2729   { TYPE_SWITCH,        &setup.autorecord,      "Auto-Record Tapes:"    },
2730   { TYPE_EMPTY,         NULL,                   ""                      },
2731   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2732
2733   { 0,                  NULL,                   NULL                    }
2734 };
2735
2736 static struct TokenInfo setup_info_editor[] =
2737 {
2738 #if 0
2739   { TYPE_SWITCH,        &setup.editor.el_boulderdash,   "Boulder Dash:" },
2740   { TYPE_SWITCH,        &setup.editor.el_emerald_mine,  "Emerald Mine:" },
2741   { TYPE_SWITCH, &setup.editor.el_emerald_mine_club,    "Emerald Mine Club:" },
2742   { TYPE_SWITCH,        &setup.editor.el_more,          "Rocks'n'Diamonds:" },
2743   { TYPE_SWITCH,        &setup.editor.el_sokoban,       "Sokoban:"      },
2744   { TYPE_SWITCH,        &setup.editor.el_supaplex,      "Supaplex:"     },
2745   { TYPE_SWITCH,        &setup.editor.el_diamond_caves, "Diamond Caves II:" },
2746   { TYPE_SWITCH,        &setup.editor.el_dx_boulderdash,"DX-Boulderdash:" },
2747 #endif
2748   { TYPE_SWITCH,        &setup.editor.el_chars,         "Text Characters:" },
2749   { TYPE_SWITCH,        &setup.editor.el_custom,  "Custom & Group Elements:" },
2750 #if 0
2751   { TYPE_SWITCH,        &setup.editor.el_headlines,     "Headlines:"    },
2752 #endif
2753   { TYPE_SWITCH, &setup.editor.el_user_defined, "User defined element list:" },
2754   { TYPE_SWITCH,        &setup.editor.el_dynamic,  "Dynamic level elements:" },
2755   { TYPE_EMPTY,         NULL,                   ""                      },
2756 #if 0
2757   { TYPE_SWITCH,        &setup.editor.el_by_game,   "Show elements by game:" },
2758   { TYPE_SWITCH,        &setup.editor.el_by_type,   "Show elements by type:" },
2759   { TYPE_EMPTY,         NULL,                   ""                      },
2760 #endif
2761   { TYPE_SWITCH, &setup.editor.show_element_token,      "Show element token:" },
2762   { TYPE_EMPTY,         NULL,                   ""                      },
2763   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2764
2765   { 0,                  NULL,                   NULL                    }
2766 };
2767
2768 static struct TokenInfo setup_info_graphics[] =
2769 {
2770   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen:"           },
2771   { TYPE_ENTER_LIST,    execSetupChooseScreenMode, "Fullscreen Mode:"   },
2772   { TYPE_STRING,        &screen_mode_text,      ""                      },
2773   { TYPE_SWITCH,        &setup.scroll_delay,    "Delayed Scrolling:"    },
2774 #if 0
2775   { TYPE_SWITCH,        &setup.soft_scrolling,  "Soft Scrolling:"       },
2776   { TYPE_SWITCH,        &setup.double_buffering,"Double-Buffering:"     },
2777   { TYPE_SWITCH,        &setup.fading,          "Fading:"               },
2778 #endif
2779   { TYPE_SWITCH,        &setup.quick_switch,    "Quick Player Focus Switch:" },
2780   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Menu Doors:"     },
2781   { TYPE_SWITCH,        &setup.show_titlescreen,"Show Title Screens:"   },
2782   { TYPE_SWITCH,        &setup.toons,           "Show Toons:"           },
2783   { TYPE_ECS_AGA,       &setup.prefer_aga_graphics,"EMC graphics preference:" },
2784   { TYPE_EMPTY,         NULL,                   ""                      },
2785   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2786
2787   { 0,                  NULL,                   NULL                    }
2788 };
2789
2790 static struct TokenInfo setup_info_sound[] =
2791 {
2792   { TYPE_SWITCH,        &setup.sound_simple,    "Sound Effects (Normal):"  },
2793   { TYPE_SWITCH,        &setup.sound_loops,     "Sound Effects (Looping):" },
2794   { TYPE_SWITCH,        &setup.sound_music,     "Music:"                },
2795   { TYPE_EMPTY,         NULL,                   ""                      },
2796   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2797
2798   { 0,                  NULL,                   NULL                    }
2799 };
2800
2801 static struct TokenInfo setup_info_artwork[] =
2802 {
2803   { TYPE_ENTER_LIST,    execSetupChooseGraphics,"Custom Graphics:"      },
2804   { TYPE_STRING,        &graphics_set_name,     ""                      },
2805   { TYPE_ENTER_LIST,    execSetupChooseSounds,  "Custom Sounds:"        },
2806   { TYPE_STRING,        &sounds_set_name,       ""                      },
2807   { TYPE_ENTER_LIST,    execSetupChooseMusic,   "Custom Music:"         },
2808   { TYPE_STRING,        &music_set_name,        ""                      },
2809   { TYPE_EMPTY,         NULL,                   ""                      },
2810 #if 1
2811   { TYPE_YES_NO, &setup.override_level_graphics,"Override Level Graphics:" },
2812   { TYPE_YES_NO, &setup.override_level_sounds,  "Override Level Sounds:"   },
2813   { TYPE_YES_NO, &setup.override_level_music,   "Override Level Music:"    },
2814 #else
2815   { TYPE_STRING,        NULL,                   "Override Level Artwork:"},
2816   { TYPE_YES_NO,        &setup.override_level_graphics, "Graphics:"     },
2817   { TYPE_YES_NO,        &setup.override_level_sounds,   "Sounds:"       },
2818   { TYPE_YES_NO,        &setup.override_level_music,    "Music:"        },
2819 #endif
2820   { TYPE_EMPTY,         NULL,                   ""                      },
2821   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2822
2823   { 0,                  NULL,                   NULL                    }
2824 };
2825
2826 static struct TokenInfo setup_info_shortcut_1[] =
2827 {
2828   { TYPE_KEYTEXT,       NULL,           "Quick Save Game to Tape:",     },
2829   { TYPE_KEY,           &setup.shortcut.save_game, ""                   },
2830   { TYPE_KEYTEXT,       NULL,           "Quick Load Game from Tape:",   },
2831   { TYPE_KEY,           &setup.shortcut.load_game, ""                   },
2832   { TYPE_KEYTEXT,       NULL,           "Start Game & Toggle Pause:",   },
2833   { TYPE_KEY,           &setup.shortcut.toggle_pause, ""                },
2834   { TYPE_EMPTY,         NULL,                   ""                      },
2835   { TYPE_YES_NO,        &setup.ask_on_escape,   "Ask on 'Esc' Key:"     },
2836   { TYPE_YES_NO, &setup.ask_on_escape_editor,   "Ask on 'Esc' Key (Editor):" },
2837   { TYPE_EMPTY,         NULL,                   ""                      },
2838   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2839
2840   { 0,                  NULL,                   NULL                    }
2841 };
2842
2843 static struct TokenInfo setup_info_shortcut_2[] =
2844 {
2845   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 1:",       },
2846   { TYPE_KEY,           &setup.shortcut.focus_player[0], ""             },
2847   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 2:",       },
2848   { TYPE_KEY,           &setup.shortcut.focus_player[1], ""             },
2849   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 3:",       },
2850   { TYPE_KEY,           &setup.shortcut.focus_player[2], ""             },
2851   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 4:",       },
2852   { TYPE_KEY,           &setup.shortcut.focus_player[3], ""             },
2853   { TYPE_KEYTEXT,       NULL,           "Set Focus to All Players:",    },
2854   { TYPE_KEY,           &setup.shortcut.focus_player_all, ""            },
2855   { TYPE_EMPTY,         NULL,                   ""                      },
2856   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2857
2858   { 0,                  NULL,                   NULL                    }
2859 };
2860
2861 static Key getSetupKey()
2862 {
2863   Key key = KSYM_UNDEFINED;
2864   boolean got_key_event = FALSE;
2865
2866   while (!got_key_event)
2867   {
2868     if (PendingEvent())         /* got event */
2869     {
2870       Event event;
2871
2872       NextEvent(&event);
2873
2874       switch(event.type)
2875       {
2876         case EVENT_KEYPRESS:
2877           {
2878             key = GetEventKey((KeyEvent *)&event, TRUE);
2879
2880             /* press 'Escape' or 'Enter' to keep the existing key binding */
2881             if (key == KSYM_Escape || key == KSYM_Return)
2882               key = KSYM_UNDEFINED;     /* keep old value */
2883
2884             got_key_event = TRUE;
2885           }
2886           break;
2887
2888         case EVENT_KEYRELEASE:
2889           key_joystick_mapping = 0;
2890           break;
2891
2892         default:
2893           HandleOtherEvents(&event);
2894           break;
2895       }
2896     }
2897
2898     DoAnimation();
2899     BackToFront();
2900
2901     /* don't eat all CPU time */
2902     Delay(10);
2903   }
2904
2905   return key;
2906 }
2907
2908 static int getSetupTextFont(int type)
2909 {
2910   if (type & (TYPE_SWITCH |
2911               TYPE_YES_NO |
2912               TYPE_STRING |
2913               TYPE_ECS_AGA |
2914               TYPE_KEYTEXT |
2915               TYPE_ENTER_LIST))
2916     return FONT_MENU_2;
2917   else
2918     return FONT_MENU_1;
2919 }
2920
2921 static int getSetupValueFont(int type, void *value)
2922 {
2923   if (type & TYPE_KEY)
2924     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
2925   else if (type & TYPE_STRING)
2926     return FONT_VALUE_2;
2927   else if (type & TYPE_ECS_AGA)
2928     return FONT_VALUE_1;
2929   else if (type & TYPE_BOOLEAN_STYLE)
2930     return (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
2931   else
2932     return FONT_VALUE_1;
2933 }
2934
2935 static void drawSetupValue(int pos)
2936 {
2937   boolean font_draw_xoffset_modified = FALSE;
2938   int font_draw_xoffset_old = -1;
2939   int xpos = MENU_SCREEN_VALUE_XPOS;
2940   int ypos = MENU_SCREEN_START_YPOS + pos;
2941   int startx = mSX + xpos * 32;
2942   int starty = mSY + ypos * 32;
2943   int font_nr, font_width;
2944   int type = setup_info[pos].type;
2945   void *value = setup_info[pos].value;
2946   char *value_string = getSetupValue(type, value);
2947   int i;
2948
2949   if (value_string == NULL)
2950     return;
2951
2952   if (type & TYPE_KEY)
2953   {
2954 #if 1
2955     xpos = MENU_SCREEN_START_XPOS;
2956 #else
2957     xpos = 3;
2958 #endif
2959
2960     if (type & TYPE_QUERY)
2961     {
2962       value_string = "<press key>";
2963 #if 0
2964       font_nr = FONT_INPUT_1_ACTIVE;
2965 #endif
2966     }
2967   }
2968   else if (type & TYPE_STRING)
2969   {
2970     int max_value_len = (SCR_FIELDX - 2) * 2;
2971
2972     xpos = MENU_SCREEN_START_XPOS;
2973 #if 0
2974     font_nr = FONT_VALUE_2;
2975 #endif
2976
2977     if (strlen(value_string) > max_value_len)
2978       value_string[max_value_len] = '\0';
2979   }
2980   else if (type & TYPE_ECS_AGA)
2981   {
2982 #if 0
2983     font_nr = FONT_VALUE_1;
2984 #endif
2985   }
2986   else if (type & TYPE_BOOLEAN_STYLE)
2987   {
2988 #if 0
2989     font_nr = (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
2990 #endif
2991   }
2992
2993   startx = mSX + xpos * 32;
2994   starty = mSY + ypos * 32;
2995   font_nr = getSetupValueFont(type, value);
2996   font_width = getFontWidth(font_nr);
2997
2998   /* downward compatibility correction for Juergen Bonhagen's menu settings */
2999   if (setup_mode != SETUP_MODE_INPUT)
3000   {
3001     int check_font_nr = FONT_OPTION_ON; /* known font that needs correction */
3002     int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
3003     int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset;
3004     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
3005     int text_font_nr = getSetupTextFont(FONT_MENU_2);
3006     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
3007     int text_width = MAX_MENU_TEXT_LENGTH_MEDIUM * getFontWidth(text_font_nr);
3008     boolean correct_font_draw_xoffset = FALSE;
3009
3010     if (xpos == MENU_SCREEN_START_XPOS &&
3011         startx + font1_xoffset < text_startx + text_font_xoffset)
3012       correct_font_draw_xoffset = TRUE;
3013
3014     if (xpos == MENU_SCREEN_VALUE_XPOS &&
3015         startx + font2_xoffset < text_startx + text_width + text_font_xoffset)
3016       correct_font_draw_xoffset = TRUE;
3017
3018 #if 0
3019     printf("::: %d + %d < %d + %d + %d\n",
3020            startx, font_xoffset, text_startx, text_width, text_font_xoffset);
3021     printf("::: => need correction == %d\n", correct_font_draw_xoffset);
3022 #endif
3023
3024     /* check if setup value would overlap with setup text when printed */
3025     /* (this can happen for extreme/wrong values for font draw offset) */
3026     if (correct_font_draw_xoffset)
3027     {
3028       font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset;
3029       font_draw_xoffset_modified = TRUE;
3030
3031       if (type & TYPE_KEY)
3032         getFontBitmapInfo(font_nr)->draw_xoffset += 2 * getFontWidth(font_nr);
3033       else if (!(type & TYPE_STRING))
3034         getFontBitmapInfo(font_nr)->draw_xoffset = text_font_xoffset + 20 -
3035           MAX_MENU_TEXT_LENGTH_MEDIUM * (16 - getFontWidth(text_font_nr));
3036     }
3037   }
3038
3039 #if 1
3040   for (i = 0; i <= MENU_SCREEN_MAX_XPOS - xpos; i++)
3041     DrawText(startx + i * font_width, starty, " ", font_nr);
3042 #else
3043 #if 1
3044   for (i = xpos; i <= MENU_SCREEN_MAX_XPOS; i++)
3045     DrawText(mSX + i * 32, starty, " ", font_nr);
3046 #else
3047   DrawText(startx, starty,
3048            (xpos == 3 ? "              " : "   "), font_nr);
3049 #endif
3050 #endif
3051
3052   DrawText(startx, starty, value_string, font_nr);
3053
3054   if (font_draw_xoffset_modified)
3055     getFontBitmapInfo(font_nr)->draw_xoffset = font_draw_xoffset_old;
3056 }
3057
3058 static void changeSetupValue(int pos)
3059 {
3060   if (setup_info[pos].type & TYPE_BOOLEAN_STYLE)
3061   {
3062     *(boolean *)setup_info[pos].value ^= TRUE;
3063   }
3064   else if (setup_info[pos].type & TYPE_KEY)
3065   {
3066     Key key;
3067
3068     setup_info[pos].type |= TYPE_QUERY;
3069     drawSetupValue(pos);
3070     setup_info[pos].type &= ~TYPE_QUERY;
3071
3072     key = getSetupKey();
3073     if (key != KSYM_UNDEFINED)
3074       *(Key *)setup_info[pos].value = key;
3075   }
3076
3077   drawSetupValue(pos);
3078 }
3079
3080 static void DrawSetupScreen_Generic()
3081 {
3082   char *title_string = NULL;
3083   int i;
3084
3085   UnmapAllGadgets();
3086   CloseDoor(DOOR_CLOSE_2);
3087
3088   ClearWindow();
3089
3090   if (setup_mode == SETUP_MODE_MAIN)
3091   {
3092     setup_info = setup_info_main;
3093     title_string = "Setup";
3094   }
3095   else if (setup_mode == SETUP_MODE_GAME)
3096   {
3097     setup_info = setup_info_game;
3098     title_string = "Setup Game";
3099   }
3100   else if (setup_mode == SETUP_MODE_EDITOR)
3101   {
3102     setup_info = setup_info_editor;
3103     title_string = "Setup Editor";
3104   }
3105   else if (setup_mode == SETUP_MODE_GRAPHICS)
3106   {
3107     setup_info = setup_info_graphics;
3108     title_string = "Setup Graphics";
3109   }
3110   else if (setup_mode == SETUP_MODE_SOUND)
3111   {
3112     setup_info = setup_info_sound;
3113     title_string = "Setup Sound";
3114   }
3115   else if (setup_mode == SETUP_MODE_ARTWORK)
3116   {
3117     setup_info = setup_info_artwork;
3118     title_string = "Custom Artwork";
3119   }
3120   else if (setup_mode == SETUP_MODE_SHORTCUT_1)
3121   {
3122     setup_info = setup_info_shortcut_1;
3123     title_string = "Setup Shortcuts";
3124   }
3125   else if (setup_mode == SETUP_MODE_SHORTCUT_2)
3126   {
3127     setup_info = setup_info_shortcut_2;
3128     title_string = "Setup Shortcuts";
3129   }
3130
3131 #if 1
3132   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
3133 #else
3134   DrawText(mSX + 16, mSY + 16, title_string, FONT_TITLE_1);
3135 #endif
3136
3137   num_setup_info = 0;
3138   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
3139   {
3140     void *value_ptr = setup_info[i].value;
3141     int xpos = MENU_SCREEN_START_XPOS;
3142     int ypos = MENU_SCREEN_START_YPOS + i;
3143 #if 1
3144     int font_nr;
3145 #else
3146     int font_nr = FONT_MENU_1;
3147 #endif
3148
3149     /* set some entries to "unchangeable" according to other variables */
3150     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
3151         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
3152         (value_ptr == &setup.sound_music  && !audio.music_available) ||
3153         (value_ptr == &setup.fullscreen   && !video.fullscreen_available) ||
3154         (value_ptr == &screen_mode_text   && !video.fullscreen_available))
3155       setup_info[i].type |= TYPE_GHOSTED;
3156
3157 #if 1
3158     font_nr = getSetupTextFont(setup_info[i].type);
3159 #else
3160 #if 1
3161     if (setup_info[i].type & (TYPE_SWITCH |
3162                               TYPE_YES_NO |
3163                               TYPE_STRING |
3164                               TYPE_ECS_AGA |
3165                               TYPE_KEYTEXT))
3166       font_nr = FONT_MENU_2;
3167 #else
3168     if (setup_info[i].type & TYPE_STRING)
3169       font_nr = FONT_MENU_2;
3170 #endif
3171 #endif
3172
3173     DrawText(mSX + xpos * 32, mSY + ypos * 32, setup_info[i].text, font_nr);
3174
3175     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
3176       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
3177     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
3178       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
3179     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
3180       initCursor(i, IMG_MENU_BUTTON);
3181
3182     if (setup_info[i].type & TYPE_VALUE)
3183       drawSetupValue(i);
3184
3185     num_setup_info++;
3186   }
3187
3188 #if 0
3189   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3190                     "Joysticks deactivated in setup menu");
3191 #endif
3192
3193   FadeToFront();
3194   InitAnimation();
3195   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
3196 }
3197
3198 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
3199 {
3200   static int choice_store[MAX_SETUP_MODES];
3201   int choice = choice_store[setup_mode];        /* always starts with 0 */
3202   int x = 0;
3203   int y = choice;
3204
3205   if (button == MB_MENU_INITIALIZE)
3206   {
3207     /* advance to first valid menu entry */
3208     while (choice < num_setup_info &&
3209            setup_info[choice].type & TYPE_SKIP_ENTRY)
3210       choice++;
3211     choice_store[setup_mode] = choice;
3212
3213     drawCursor(choice, FC_RED);
3214
3215     return;
3216   }
3217   else if (button == MB_MENU_LEAVE)
3218   {
3219     for (y = 0; y < num_setup_info; y++)
3220     {
3221       if (setup_info[y].type & TYPE_LEAVE_MENU)
3222       {
3223         void (*menu_callback_function)(void) = setup_info[y].value;
3224
3225         menu_callback_function();
3226
3227         break;  /* absolutely needed because function changes 'setup_info'! */
3228       }
3229     }
3230
3231     return;
3232   }
3233
3234   if (mx || my)         /* mouse input */
3235   {
3236     x = (mx - mSX) / 32;
3237     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3238   }
3239   else if (dx || dy)    /* keyboard input */
3240   {
3241     if (dx)
3242     {
3243       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
3244
3245       if (setup_info[choice].type & menu_navigation_type ||
3246           setup_info[choice].type & TYPE_BOOLEAN_STYLE)
3247         button = MB_MENU_CHOICE;
3248     }
3249     else if (dy)
3250       y = choice + dy;
3251
3252     /* jump to next non-empty menu entry (up or down) */
3253     while (y > 0 && y < num_setup_info - 1 &&
3254            setup_info[y].type & TYPE_SKIP_ENTRY)
3255       y += dy;
3256   }
3257
3258 #if 1
3259   if (IN_VIS_FIELD(x, y) && y >= 0 && y < num_setup_info)
3260   {
3261     if (button)
3262     {
3263       if (y != choice && setup_info[y].type & ~TYPE_SKIP_ENTRY)
3264       {
3265         drawCursor(y, FC_RED);
3266         drawCursor(choice, FC_BLUE);
3267         choice = choice_store[setup_mode] = y;
3268       }
3269     }
3270     else if (!(setup_info[y].type & TYPE_GHOSTED))
3271     {
3272       /* when selecting key headline, execute function for key value change */
3273       if (setup_info[y].type & TYPE_KEYTEXT &&
3274           setup_info[y + 1].type & TYPE_KEY)
3275         y++;
3276
3277       /* when selecting string value, execute function for list selection */
3278       if (setup_info[y].type & TYPE_STRING && y > 0 &&
3279           setup_info[y - 1].type & TYPE_ENTER_LIST)
3280         y--;
3281
3282       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE)
3283       {
3284         void (*menu_callback_function)(void) = setup_info[y].value;
3285
3286         menu_callback_function();
3287       }
3288       else
3289       {
3290         if (setup_info[y].type & TYPE_VALUE)
3291           changeSetupValue(y);
3292       }
3293     }
3294   }
3295 #else
3296   if (IN_VIS_FIELD(x, y) &&
3297       y >= 0 && y < num_setup_info && setup_info[y].type & ~TYPE_SKIP_ENTRY)
3298   {
3299     if (button)
3300     {
3301       if (y != choice)
3302       {
3303         drawCursor(y, FC_RED);
3304         drawCursor(choice, FC_BLUE);
3305         choice = choice_store[setup_mode] = y;
3306       }
3307     }
3308     else if (!(setup_info[y].type & TYPE_GHOSTED))
3309     {
3310       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE)
3311       {
3312         void (*menu_callback_function)(void) = setup_info[choice].value;
3313
3314         menu_callback_function();
3315       }
3316       else
3317       {
3318         if (setup_info[y].type & TYPE_KEYTEXT &&
3319             setup_info[y + 1].type & TYPE_KEY)
3320           y++;
3321
3322         if (setup_info[y].type & TYPE_VALUE)
3323           changeSetupValue(y);
3324       }
3325     }
3326   }
3327 #endif
3328 }
3329
3330 void DrawSetupScreen_Input()
3331 {
3332   ClearWindow();
3333
3334 #if 1
3335   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Setup Input");
3336 #else
3337   DrawText(mSX + 16, mSY + 16, "Setup Input", FONT_TITLE_1);
3338 #endif
3339
3340   initCursor(0,  IMG_MENU_BUTTON);
3341   initCursor(1,  IMG_MENU_BUTTON);
3342   initCursor(2,  IMG_MENU_BUTTON_ENTER_MENU);
3343   initCursor(13, IMG_MENU_BUTTON_LEAVE_MENU);
3344
3345 #if 0
3346   drawCursorXY(10, 0, IMG_MENU_BUTTON_LEFT);
3347   drawCursorXY(12, 0, IMG_MENU_BUTTON_RIGHT);
3348 #endif
3349
3350   DrawText(mSX + 32, mSY +  2 * 32, "Player:", FONT_MENU_1);
3351   DrawText(mSX + 32, mSY +  3 * 32, "Device:", FONT_MENU_1);
3352   DrawText(mSX + 32, mSY + 15 * 32, "Back",   FONT_MENU_1);
3353
3354 #if 0
3355   DeactivateJoystickForCalibration();
3356 #endif
3357 #if 1
3358   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3359                     "Joysticks deactivated on this screen");
3360 #endif
3361
3362 #if 1
3363   /* create gadgets for setup input menu screen */
3364   FreeScreenGadgets();
3365   CreateScreenGadgets();
3366
3367   /* map gadgets for setup input menu screen */
3368   MapScreenMenuGadgets(SCREEN_MASK_INPUT);
3369 #endif
3370
3371   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
3372   FadeToFront();
3373   InitAnimation();
3374 }
3375
3376 static void setJoystickDeviceToNr(char *device_name, int device_nr)
3377 {
3378   if (device_name == NULL)
3379     return;
3380
3381   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
3382     device_nr = 0;
3383
3384   if (strlen(device_name) > 1)
3385   {
3386     char c1 = device_name[strlen(device_name) - 1];
3387     char c2 = device_name[strlen(device_name) - 2];
3388
3389     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
3390       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
3391   }
3392   else
3393     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
3394             strlen(device_name));
3395 }
3396
3397 static void drawPlayerSetupInputInfo(int player_nr)
3398 {
3399   int i;
3400   static struct SetupKeyboardInfo custom_key;
3401   static struct
3402   {
3403     Key *key;
3404     char *text;
3405   } custom[] =
3406   {
3407     { &custom_key.left,  "Joystick Left"  },
3408     { &custom_key.right, "Joystick Right" },
3409     { &custom_key.up,    "Joystick Up"    },
3410     { &custom_key.down,  "Joystick Down"  },
3411     { &custom_key.snap,  "Button 1"       },
3412     { &custom_key.drop,  "Button 2"       }
3413   };
3414   static char *joystick_name[MAX_PLAYERS] =
3415   {
3416     "Joystick1",
3417     "Joystick2",
3418     "Joystick3",
3419     "Joystick4"
3420   };
3421
3422   InitJoysticks();
3423
3424   custom_key = setup.input[player_nr].key;
3425
3426   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
3427            FONT_INPUT_1_ACTIVE);
3428
3429   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3430                              TILEX, TILEY);
3431   DrawGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3432                          PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
3433
3434   if (setup.input[player_nr].use_joystick)
3435   {
3436     char *device_name = setup.input[player_nr].joy.device_name;
3437     char *text = joystick_name[getJoystickNrFromDeviceName(device_name)];
3438     int font_nr = (joystick.fd[player_nr] < 0 ? FONT_VALUE_OLD : FONT_VALUE_1);
3439
3440     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
3441     DrawText(mSX + 32, mSY + 4 * 32, "Calibrate", FONT_MENU_1);
3442   }
3443   else
3444   {
3445     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", FONT_VALUE_1);
3446     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", FONT_MENU_1);
3447   }
3448
3449   DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", FONT_MENU_1);
3450   drawCursorXY(1, 4, IMG_MENU_BUTTON_LEFT);
3451   drawCursorXY(1, 5, IMG_MENU_BUTTON_RIGHT);
3452   drawCursorXY(1, 6, IMG_MENU_BUTTON_UP);
3453   drawCursorXY(1, 7, IMG_MENU_BUTTON_DOWN);
3454   DrawText(mSX + 2 * 32, mSY +  6 * 32, ":", FONT_VALUE_OLD);
3455   DrawText(mSX + 2 * 32, mSY +  7 * 32, ":", FONT_VALUE_OLD);
3456   DrawText(mSX + 2 * 32, mSY +  8 * 32, ":", FONT_VALUE_OLD);
3457   DrawText(mSX + 2 * 32, mSY +  9 * 32, ":", FONT_VALUE_OLD);
3458   DrawText(mSX + 1 * 32, mSY + 10 * 32, "Snap Field:", FONT_VALUE_OLD);
3459   DrawText(mSX + 1 * 32, mSY + 12 * 32, "Drop Element:", FONT_VALUE_OLD);
3460
3461   for (i = 0; i < 6; i++)
3462   {
3463     int ypos = 6 + i + (i > 3 ? i-3 : 0);
3464
3465     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3466              "              ", FONT_VALUE_1);
3467     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3468              (setup.input[player_nr].use_joystick ?
3469               custom[i].text :
3470               getKeyNameFromKey(*custom[i].key)), FONT_VALUE_1);
3471   }
3472 }
3473
3474 static int input_player_nr = 0;
3475
3476 void HandleSetupScreen_Input_Player(int step, int direction)
3477 {
3478   int old_player_nr = input_player_nr;
3479   int new_player_nr;
3480
3481   new_player_nr = old_player_nr + step * direction;
3482   if (new_player_nr < 0)
3483     new_player_nr = 0;
3484   if (new_player_nr > MAX_PLAYERS - 1)
3485     new_player_nr = MAX_PLAYERS - 1;
3486
3487   if (new_player_nr != old_player_nr)
3488   {
3489     input_player_nr = new_player_nr;
3490
3491     drawPlayerSetupInputInfo(input_player_nr);
3492   }
3493 }
3494
3495 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
3496 {
3497   static int choice = 0;
3498   int x = 0;
3499   int y = choice;
3500   int pos_start  = SETUPINPUT_SCREEN_POS_START;
3501   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
3502   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
3503   int pos_end    = SETUPINPUT_SCREEN_POS_END;
3504
3505   if (button == MB_MENU_INITIALIZE)
3506   {
3507     drawPlayerSetupInputInfo(input_player_nr);
3508     drawCursor(choice, FC_RED);
3509
3510     return;
3511   }
3512   else if (button == MB_MENU_LEAVE)
3513   {
3514     setup_mode = SETUP_MODE_MAIN;
3515     DrawSetupScreen();
3516     InitJoysticks();
3517
3518     return;
3519   }
3520
3521   if (mx || my)         /* mouse input */
3522   {
3523     x = (mx - mSX) / 32;
3524     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3525   }
3526   else if (dx || dy)    /* keyboard input */
3527   {
3528     if (dx && choice == 0)
3529       x = (dx < 0 ? 10 : 12);
3530     else if ((dx && choice == 1) ||
3531              (dx == +1 && choice == 2) ||
3532              (dx == -1 && choice == pos_end))
3533       button = MB_MENU_CHOICE;
3534     else if (dy)
3535       y = choice + dy;
3536
3537     if (y >= pos_empty1 && y <= pos_empty2)
3538       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
3539   }
3540
3541 #if 1
3542
3543   if (y == 0 && dx != 0 && button)
3544   {
3545     HandleSetupScreen_Input_Player(1, dx < 0 ? -1 : +1);
3546   }
3547
3548 #else
3549
3550   if (IN_VIS_FIELD(x, y) &&
3551       y == 0 && ((x < 10 && !button) || ((x == 10 || x == 12) && button)))
3552   {
3553     static unsigned long delay = 0;
3554
3555     if (!DelayReached(&delay, GADGET_FRAME_DELAY))
3556       return;
3557
3558     input_player_nr =
3559       (input_player_nr + (x == 10 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
3560
3561     drawPlayerSetupInputInfo(input_player_nr);
3562   }
3563
3564 #endif
3565
3566   else if (IN_VIS_FIELD(x, y) &&
3567            y >= pos_start && y <= pos_end &&
3568            !(y >= pos_empty1 && y <= pos_empty2))
3569   {
3570     if (button)
3571     {
3572       if (y != choice)
3573       {
3574         drawCursor(y, FC_RED);
3575         drawCursor(choice, FC_BLUE);
3576         choice = y;
3577       }
3578     }
3579     else
3580     {
3581       if (y == 1)
3582       {
3583         char *device_name = setup.input[input_player_nr].joy.device_name;
3584
3585         if (!setup.input[input_player_nr].use_joystick)
3586         {
3587           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
3588
3589           setJoystickDeviceToNr(device_name, new_device_nr);
3590           setup.input[input_player_nr].use_joystick = TRUE;
3591         }
3592         else
3593         {
3594           int device_nr = getJoystickNrFromDeviceName(device_name);
3595           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
3596
3597           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
3598             setup.input[input_player_nr].use_joystick = FALSE;
3599           else
3600             setJoystickDeviceToNr(device_name, new_device_nr);
3601         }
3602
3603         drawPlayerSetupInputInfo(input_player_nr);
3604       }
3605       else if (y == 2)
3606       {
3607         if (setup.input[input_player_nr].use_joystick)
3608         {
3609           InitJoysticks();
3610           CalibrateJoystick(input_player_nr);
3611         }
3612         else
3613           CustomizeKeyboard(input_player_nr);
3614       }
3615       else if (y == pos_end)
3616       {
3617         InitJoysticks();
3618
3619         setup_mode = SETUP_MODE_MAIN;
3620         DrawSetupScreen();
3621       }
3622     }
3623   }
3624 }
3625
3626 void CustomizeKeyboard(int player_nr)
3627 {
3628   int i;
3629   int step_nr;
3630   boolean finished = FALSE;
3631   static struct SetupKeyboardInfo custom_key;
3632   static struct
3633   {
3634     Key *key;
3635     char *text;
3636   } customize_step[] =
3637   {
3638     { &custom_key.left,  "Move Left"    },
3639     { &custom_key.right, "Move Right"   },
3640     { &custom_key.up,    "Move Up"      },
3641     { &custom_key.down,  "Move Down"    },
3642     { &custom_key.snap,  "Snap Field"   },
3643     { &custom_key.drop,  "Drop Element" }
3644   };
3645
3646   /* read existing key bindings from player setup */
3647   custom_key = setup.input[player_nr].key;
3648
3649   ClearWindow();
3650
3651 #if 1
3652   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
3653 #else
3654   DrawText(mSX + 16, mSY + 16, "Keyboard Input", FONT_TITLE_1);
3655 #endif
3656
3657   BackToFront();
3658   InitAnimation();
3659
3660   step_nr = 0;
3661   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
3662            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
3663   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
3664            "Key:", FONT_INPUT_1_ACTIVE);
3665   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3666            getKeyNameFromKey(*customize_step[step_nr].key), FONT_VALUE_OLD);
3667
3668   while (!finished)
3669   {
3670     if (PendingEvent())         /* got event */
3671     {
3672       Event event;
3673
3674       NextEvent(&event);
3675
3676       switch(event.type)
3677       {
3678         case EVENT_KEYPRESS:
3679           {
3680             Key key = GetEventKey((KeyEvent *)&event, FALSE);
3681
3682             if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
3683             {
3684               finished = TRUE;
3685               break;
3686             }
3687
3688             /* all keys configured -- wait for "Escape" or "Return" key */
3689             if (step_nr == 6)
3690               break;
3691
3692             /* press 'Enter' to keep the existing key binding */
3693             if (key == KSYM_Return)
3694               key = *customize_step[step_nr].key;
3695
3696             /* check if key already used */
3697             for (i = 0; i < step_nr; i++)
3698               if (*customize_step[i].key == key)
3699                 break;
3700             if (i < step_nr)
3701               break;
3702
3703             /* got new key binding */
3704             *customize_step[step_nr].key = key;
3705             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3706                      "             ", FONT_VALUE_1);
3707             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3708                      getKeyNameFromKey(key), FONT_VALUE_1);
3709             step_nr++;
3710
3711             /* un-highlight last query */
3712             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
3713                      customize_step[step_nr - 1].text, FONT_MENU_1);
3714             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
3715                      "Key:", FONT_MENU_1);
3716
3717             /* press 'Enter' to leave */
3718             if (step_nr == 6)
3719             {
3720               DrawText(mSX + 16, mSY + 15 * 32 + 16,
3721                        "Press Enter", FONT_TITLE_1);
3722               break;
3723             }
3724
3725             /* query next key binding */
3726             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
3727                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
3728             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
3729                      "Key:", FONT_INPUT_1_ACTIVE);
3730             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3731                      getKeyNameFromKey(*customize_step[step_nr].key),
3732                      FONT_VALUE_OLD);
3733           }
3734           break;
3735
3736         case EVENT_KEYRELEASE:
3737           key_joystick_mapping = 0;
3738           break;
3739
3740         default:
3741           HandleOtherEvents(&event);
3742           break;
3743       }
3744     }
3745
3746     DoAnimation();
3747     BackToFront();
3748
3749     /* don't eat all CPU time */
3750     Delay(10);
3751   }
3752
3753   /* write new key bindings back to player setup */
3754   setup.input[player_nr].key = custom_key;
3755
3756   StopAnimation();
3757   DrawSetupScreen_Input();
3758 }
3759
3760 static boolean CalibrateJoystickMain(int player_nr)
3761 {
3762   int new_joystick_xleft = JOYSTICK_XMIDDLE;
3763   int new_joystick_xright = JOYSTICK_XMIDDLE;
3764   int new_joystick_yupper = JOYSTICK_YMIDDLE;
3765   int new_joystick_ylower = JOYSTICK_YMIDDLE;
3766   int new_joystick_xmiddle, new_joystick_ymiddle;
3767
3768   int joystick_fd = joystick.fd[player_nr];
3769   int x, y, last_x, last_y, xpos = 8, ypos = 3;
3770   boolean check[3][3];
3771   int check_remaining = 3 * 3;
3772   int joy_x, joy_y;
3773   int joy_value;
3774   int result = -1;
3775
3776   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
3777     return FALSE;
3778
3779   if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
3780     return FALSE;
3781
3782   ClearWindow();
3783
3784   for (y = 0; y < 3; y++)
3785   {
3786     for (x = 0; x < 3; x++)
3787     {
3788       DrawGraphic(xpos + x - 1, ypos + y - 1, IMG_MENU_CALIBRATE_BLUE, 0);
3789       check[x][y] = FALSE;
3790     }
3791   }
3792
3793   DrawTextSCentered(mSY - SY +  6 * 32, FONT_TITLE_1, "Rotate joystick");
3794   DrawTextSCentered(mSY - SY +  7 * 32, FONT_TITLE_1, "in all directions");
3795   DrawTextSCentered(mSY - SY +  9 * 32, FONT_TITLE_1, "if all balls");
3796   DrawTextSCentered(mSY - SY + 10 * 32, FONT_TITLE_1, "are marked,");
3797   DrawTextSCentered(mSY - SY + 11 * 32, FONT_TITLE_1, "center joystick");
3798   DrawTextSCentered(mSY - SY + 12 * 32, FONT_TITLE_1, "and");
3799   DrawTextSCentered(mSY - SY + 13 * 32, FONT_TITLE_1, "press any button!");
3800
3801   joy_value = Joystick(player_nr);
3802   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
3803   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
3804
3805   /* eventually uncalibrated center position (joystick could be uncentered) */
3806   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3807     return FALSE;
3808
3809   new_joystick_xmiddle = joy_x;
3810   new_joystick_ymiddle = joy_y;
3811
3812   DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_RED, 0);
3813   BackToFront();
3814
3815   while (Joystick(player_nr) & JOY_BUTTON);     /* wait for released button */
3816   InitAnimation();
3817
3818   while (result < 0)
3819   {
3820     if (PendingEvent())         /* got event */
3821     {
3822       Event event;
3823
3824       NextEvent(&event);
3825
3826       switch(event.type)
3827       {
3828         case EVENT_KEYPRESS:
3829           switch(GetEventKey((KeyEvent *)&event, TRUE))
3830           {
3831             case KSYM_Return:
3832               if (check_remaining == 0)
3833                 result = 1;
3834               break;
3835
3836             case KSYM_Escape:
3837               result = 0;
3838               break;
3839
3840             default:
3841               break;
3842           }
3843           break;
3844
3845         case EVENT_KEYRELEASE:
3846           key_joystick_mapping = 0;
3847           break;
3848
3849         default:
3850           HandleOtherEvents(&event);
3851           break;
3852       }
3853     }
3854
3855     if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3856       return FALSE;
3857
3858     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
3859     new_joystick_xright = MAX(new_joystick_xright, joy_x);
3860     new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
3861     new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
3862
3863     setup.input[player_nr].joy.xleft = new_joystick_xleft;
3864     setup.input[player_nr].joy.yupper = new_joystick_yupper;
3865     setup.input[player_nr].joy.xright = new_joystick_xright;
3866     setup.input[player_nr].joy.ylower = new_joystick_ylower;
3867     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
3868     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
3869
3870     CheckJoystickData();
3871
3872     joy_value = Joystick(player_nr);
3873
3874     if (joy_value & JOY_BUTTON && check_remaining == 0)
3875       result = 1;
3876
3877     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
3878     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
3879
3880     if (x != last_x || y != last_y)
3881     {
3882       DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_YELLOW, 0);
3883       DrawGraphic(xpos + x,      ypos + y,      IMG_MENU_CALIBRATE_RED,    0);
3884
3885       last_x = x;
3886       last_y = y;
3887
3888       if (check_remaining > 0 && !check[x+1][y+1])
3889       {
3890         check[x+1][y+1] = TRUE;
3891         check_remaining--;
3892       }
3893
3894 #if 0
3895 #ifdef DEBUG
3896       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
3897              setup.input[player_nr].joy.xleft,
3898              setup.input[player_nr].joy.xmiddle,
3899              setup.input[player_nr].joy.xright);
3900       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
3901              setup.input[player_nr].joy.yupper,
3902              setup.input[player_nr].joy.ymiddle,
3903              setup.input[player_nr].joy.ylower);
3904 #endif
3905 #endif
3906
3907     }
3908
3909     DoAnimation();
3910     BackToFront();
3911
3912     /* don't eat all CPU time */
3913     Delay(10);
3914   }
3915
3916   /* calibrated center position (joystick should now be centered) */
3917   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
3918     return FALSE;
3919
3920   new_joystick_xmiddle = joy_x;
3921   new_joystick_ymiddle = joy_y;
3922
3923   StopAnimation();
3924
3925 #if 0
3926   DrawSetupScreen_Input();
3927 #endif
3928
3929   /* wait until the last pressed button was released */
3930   while (Joystick(player_nr) & JOY_BUTTON)
3931   {
3932     if (PendingEvent())         /* got event */
3933     {
3934       Event event;
3935
3936       NextEvent(&event);
3937       HandleOtherEvents(&event);
3938
3939       Delay(10);
3940     }
3941   }
3942
3943   return TRUE;
3944 }
3945
3946 void CalibrateJoystick(int player_nr)
3947 {
3948   if (!CalibrateJoystickMain(player_nr))
3949   {
3950     char *device_name = setup.input[player_nr].joy.device_name;
3951     int nr = getJoystickNrFromDeviceName(device_name) + 1;
3952     int xpos = mSX - SX;
3953     int ypos = mSY - SY;
3954
3955     ClearWindow();
3956
3957     DrawTextF(xpos + 16, ypos + 6 * 32, FONT_TITLE_1, "   JOYSTICK %d   ", nr);
3958     DrawTextF(xpos + 16, ypos + 7 * 32, FONT_TITLE_1, " NOT AVAILABLE! ");
3959     BackToFront();
3960
3961     Delay(2000);                /* show error message for a short time */
3962
3963     ClearEventQueue();
3964   }
3965
3966 #if 1
3967   DrawSetupScreen_Input();
3968 #endif
3969 }
3970
3971 void DrawSetupScreen()
3972 {
3973   DeactivateJoystick();
3974
3975   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
3976
3977   if (setup_mode == SETUP_MODE_INPUT)
3978     DrawSetupScreen_Input();
3979   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
3980     DrawChooseTree(&screen_mode_current);
3981   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
3982     DrawChooseTree(&artwork.gfx_current);
3983   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
3984     DrawChooseTree(&artwork.snd_current);
3985   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
3986     DrawChooseTree(&artwork.mus_current);
3987   else
3988     DrawSetupScreen_Generic();
3989
3990   PlayMenuSound();
3991   PlayMenuMusic();
3992 }
3993
3994 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
3995 {
3996   if (setup_mode == SETUP_MODE_INPUT)
3997     HandleSetupScreen_Input(mx, my, dx, dy, button);
3998   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
3999     HandleChooseTree(mx, my, dx, dy, button, &screen_mode_current);
4000   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
4001     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
4002   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
4003     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
4004   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
4005     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
4006   else
4007     HandleSetupScreen_Generic(mx, my, dx, dy, button);
4008
4009   DoAnimation();
4010 }
4011
4012 void HandleGameActions()
4013 {
4014   if (game_status != GAME_MODE_PLAYING)
4015     return;
4016
4017   GameActions();        /* main game loop */
4018
4019   if (tape.auto_play && !tape.playing)
4020     AutoPlayTape();     /* continue automatically playing next tape */
4021 }
4022
4023
4024 /* ---------- new screen button stuff -------------------------------------- */
4025
4026 static void getScreenMenuButtonPos(int *x, int *y, int gadget_id)
4027 {
4028   switch (gadget_id)
4029   {
4030     case SCREEN_CTRL_ID_LAST_LEVEL:
4031       *x = mSX + TILEX * getLastLevelButtonPos();
4032       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
4033       break;
4034
4035     case SCREEN_CTRL_ID_NEXT_LEVEL:
4036       *x = mSX + TILEX * getNextLevelButtonPos();
4037       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
4038       break;
4039
4040     case SCREEN_CTRL_ID_LAST_PLAYER:
4041       *x = mSX + TILEX * 10;
4042       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
4043       break;
4044
4045     case SCREEN_CTRL_ID_NEXT_PLAYER:
4046       *x = mSX + TILEX * 12;
4047       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
4048       break;
4049
4050     default:
4051       Error(ERR_EXIT, "unknown gadget ID %d", gadget_id);
4052   }
4053 }
4054
4055 static struct
4056 {
4057   int gfx_unpressed, gfx_pressed;
4058   void (*get_gadget_position)(int *, int *, int);
4059   int gadget_id;
4060   int screen_mask;
4061   char *infotext;
4062 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
4063 {
4064   {
4065     IMG_MENU_BUTTON_LAST_LEVEL, IMG_MENU_BUTTON_LAST_LEVEL_ACTIVE,
4066     getScreenMenuButtonPos,
4067     SCREEN_CTRL_ID_LAST_LEVEL,
4068     SCREEN_MASK_MAIN,
4069     "last level"
4070   },
4071   {
4072     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
4073     getScreenMenuButtonPos,
4074     SCREEN_CTRL_ID_NEXT_LEVEL,
4075     SCREEN_MASK_MAIN,
4076     "next level"
4077   },
4078   {
4079     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
4080     getScreenMenuButtonPos,
4081     SCREEN_CTRL_ID_LAST_PLAYER,
4082     SCREEN_MASK_INPUT,
4083     "last player"
4084   },
4085   {
4086     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
4087     getScreenMenuButtonPos,
4088     SCREEN_CTRL_ID_NEXT_PLAYER,
4089     SCREEN_MASK_INPUT,
4090     "next player"
4091   },
4092 };
4093
4094 static struct
4095 {
4096   int gfx_unpressed, gfx_pressed;
4097   int x, y;
4098   int gadget_id;
4099   char *infotext;
4100 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
4101 {
4102   {
4103     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
4104     SC_SCROLL_UP_XPOS, SC_SCROLL_UP_YPOS,
4105     SCREEN_CTRL_ID_SCROLL_UP,
4106     "scroll up"
4107   },
4108   {
4109     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
4110     SC_SCROLL_DOWN_XPOS, SC_SCROLL_DOWN_YPOS,
4111     SCREEN_CTRL_ID_SCROLL_DOWN,
4112     "scroll down"
4113   }
4114 };
4115
4116 static struct
4117 {
4118 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4119   Bitmap **gfx_unpressed, **gfx_pressed;
4120 #else
4121   int gfx_unpressed, gfx_pressed;
4122 #endif
4123   int x, y;
4124   int width, height;
4125   int type;
4126   int gadget_id;
4127   char *infotext;
4128 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
4129 {
4130   {
4131 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4132     &scrollbar_bitmap[0], &scrollbar_bitmap[1],
4133 #else
4134     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
4135 #endif
4136     SC_SCROLL_VERTICAL_XPOS, SC_SCROLL_VERTICAL_YPOS,
4137     SC_SCROLL_VERTICAL_XSIZE, SC_SCROLL_VERTICAL_YSIZE,
4138     GD_TYPE_SCROLLBAR_VERTICAL,
4139     SCREEN_CTRL_ID_SCROLL_VERTICAL,
4140     "scroll level series vertically"
4141   }
4142 };
4143
4144 static void CreateScreenMenubuttons()
4145 {
4146   struct GadgetInfo *gi;
4147   unsigned long event_mask;
4148   int i;
4149
4150   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
4151   {
4152     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4153     int gfx_unpressed, gfx_pressed;
4154     int x, y, width, height;
4155     int gd_x1, gd_x2, gd_y1, gd_y2;
4156     int id = menubutton_info[i].gadget_id;
4157
4158     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
4159
4160     menubutton_info[i].get_gadget_position(&x, &y, id);
4161
4162     width = SC_MENUBUTTON_XSIZE;
4163     height = SC_MENUBUTTON_YSIZE;
4164
4165     gfx_unpressed = menubutton_info[i].gfx_unpressed;
4166     gfx_pressed   = menubutton_info[i].gfx_pressed;
4167     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4168     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4169     gd_x1 = graphic_info[gfx_unpressed].src_x;
4170     gd_y1 = graphic_info[gfx_unpressed].src_y;
4171     gd_x2 = graphic_info[gfx_pressed].src_x;
4172     gd_y2 = graphic_info[gfx_pressed].src_y;
4173
4174     gi = CreateGadget(GDI_CUSTOM_ID, id,
4175                       GDI_CUSTOM_TYPE_ID, i,
4176                       GDI_INFO_TEXT, menubutton_info[i].infotext,
4177                       GDI_X, x,
4178                       GDI_Y, y,
4179                       GDI_WIDTH, width,
4180                       GDI_HEIGHT, height,
4181                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4182                       GDI_STATE, GD_BUTTON_UNPRESSED,
4183                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4184                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4185                       GDI_DIRECT_DRAW, FALSE,
4186                       GDI_EVENT_MASK, event_mask,
4187                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4188                       GDI_END);
4189
4190     if (gi == NULL)
4191       Error(ERR_EXIT, "cannot create gadget");
4192
4193     screen_gadget[id] = gi;
4194   }
4195 }
4196
4197 static void CreateScreenScrollbuttons()
4198 {
4199   struct GadgetInfo *gi;
4200   unsigned long event_mask;
4201   int i;
4202
4203   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
4204   {
4205     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4206     int gfx_unpressed, gfx_pressed;
4207     int x, y, width, height;
4208     int gd_x1, gd_x2, gd_y1, gd_y2;
4209     int id = scrollbutton_info[i].gadget_id;
4210
4211     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
4212
4213     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
4214     y = mSY + scrollbutton_info[i].y;
4215     width = SC_SCROLLBUTTON_XSIZE;
4216     height = SC_SCROLLBUTTON_YSIZE;
4217
4218     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
4219       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
4220                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
4221
4222     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
4223     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
4224     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4225     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4226     gd_x1 = graphic_info[gfx_unpressed].src_x;
4227     gd_y1 = graphic_info[gfx_unpressed].src_y;
4228     gd_x2 = graphic_info[gfx_pressed].src_x;
4229     gd_y2 = graphic_info[gfx_pressed].src_y;
4230
4231     gi = CreateGadget(GDI_CUSTOM_ID, id,
4232                       GDI_CUSTOM_TYPE_ID, i,
4233                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
4234                       GDI_X, x,
4235                       GDI_Y, y,
4236                       GDI_WIDTH, width,
4237                       GDI_HEIGHT, height,
4238                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4239                       GDI_STATE, GD_BUTTON_UNPRESSED,
4240                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4241                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4242                       GDI_DIRECT_DRAW, FALSE,
4243                       GDI_EVENT_MASK, event_mask,
4244                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4245                       GDI_END);
4246
4247     if (gi == NULL)
4248       Error(ERR_EXIT, "cannot create gadget");
4249
4250     screen_gadget[id] = gi;
4251   }
4252 }
4253
4254 static void CreateScreenScrollbars()
4255 {
4256   int i;
4257
4258   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
4259   {
4260     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4261 #if !defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4262     int gfx_unpressed, gfx_pressed;
4263 #endif
4264     int x, y, width, height;
4265     int gd_x1, gd_x2, gd_y1, gd_y2;
4266     struct GadgetInfo *gi;
4267     int items_max, items_visible, item_position;
4268     unsigned long event_mask;
4269     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
4270     int id = scrollbar_info[i].gadget_id;
4271
4272     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
4273
4274     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
4275     y = mSY + scrollbar_info[i].y;
4276     width  = scrollbar_info[i].width;
4277     height = scrollbar_info[i].height;
4278
4279     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
4280       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
4281
4282     items_max = num_page_entries;
4283     items_visible = num_page_entries;
4284     item_position = 0;
4285
4286 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4287     gd_bitmap_unpressed = *scrollbar_info[i].gfx_unpressed;
4288     gd_bitmap_pressed   = *scrollbar_info[i].gfx_pressed;
4289     gd_x1 = 0;
4290     gd_y1 = 0;
4291     gd_x2 = 0;
4292     gd_y2 = 0;
4293 #else
4294     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
4295     gfx_pressed   = scrollbar_info[i].gfx_pressed;
4296     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4297     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4298     gd_x1 = graphic_info[gfx_unpressed].src_x;
4299     gd_y1 = graphic_info[gfx_unpressed].src_y;
4300     gd_x2 = graphic_info[gfx_pressed].src_x;
4301     gd_y2 = graphic_info[gfx_pressed].src_y;
4302 #endif
4303
4304     gi = CreateGadget(GDI_CUSTOM_ID, id,
4305                       GDI_CUSTOM_TYPE_ID, i,
4306                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
4307                       GDI_X, x,
4308                       GDI_Y, y,
4309                       GDI_WIDTH, width,
4310                       GDI_HEIGHT, height,
4311                       GDI_TYPE, scrollbar_info[i].type,
4312                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
4313                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
4314                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
4315                       GDI_WHEEL_AREA_X, 0,
4316                       GDI_WHEEL_AREA_Y, 0,
4317                       GDI_WHEEL_AREA_WIDTH, WIN_XSIZE,
4318                       GDI_WHEEL_AREA_HEIGHT, WIN_YSIZE,
4319                       GDI_STATE, GD_BUTTON_UNPRESSED,
4320                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4321                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4322                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
4323                       GDI_DIRECT_DRAW, FALSE,
4324                       GDI_EVENT_MASK, event_mask,
4325                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4326                       GDI_END);
4327
4328     if (gi == NULL)
4329       Error(ERR_EXIT, "cannot create gadget");
4330
4331     screen_gadget[id] = gi;
4332   }
4333 }
4334
4335 void CreateScreenGadgets()
4336 {
4337   int last_game_status = game_status;   /* save current game status */
4338
4339 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4340   int i;
4341
4342   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
4343   {
4344     scrollbar_bitmap[i] = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
4345
4346     /* copy pointers to clip mask and GC */
4347     scrollbar_bitmap[i]->clip_mask =
4348       graphic_info[IMG_MENU_SCROLLBAR + i].clip_mask;
4349     scrollbar_bitmap[i]->stored_clip_gc =
4350       graphic_info[IMG_MENU_SCROLLBAR + i].clip_gc;
4351
4352     BlitBitmap(graphic_info[IMG_MENU_SCROLLBAR + i].bitmap,
4353                scrollbar_bitmap[i],
4354                graphic_info[IMG_MENU_SCROLLBAR + i].src_x,
4355                graphic_info[IMG_MENU_SCROLLBAR + i].src_y,
4356                TILEX, TILEY, 0, 0);
4357   }
4358 #endif
4359
4360   CreateScreenMenubuttons();
4361
4362   /* force LEVELS draw offset for scrollbar / scrollbutton gadgets */
4363   game_status = GAME_MODE_LEVELS;
4364
4365   CreateScreenScrollbuttons();
4366   CreateScreenScrollbars();
4367
4368   game_status = last_game_status;       /* restore current game status */
4369 }
4370
4371 void FreeScreenGadgets()
4372 {
4373   int i;
4374
4375 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4376   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
4377   {
4378     /* prevent freeing clip mask and GC twice */
4379     scrollbar_bitmap[i]->clip_mask = None;
4380     scrollbar_bitmap[i]->stored_clip_gc = None;
4381
4382     FreeBitmap(scrollbar_bitmap[i]);
4383   }
4384 #endif
4385
4386   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
4387     FreeGadget(screen_gadget[i]);
4388 }
4389
4390 void MapScreenMenuGadgets(int screen_mask)
4391 {
4392   int i;
4393
4394   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
4395     if (screen_mask & menubutton_info[i].screen_mask)
4396       MapGadget(screen_gadget[menubutton_info[i].gadget_id]);
4397 }
4398
4399 void MapScreenTreeGadgets(TreeInfo *ti)
4400 {
4401   int num_entries = numTreeInfoInGroup(ti);
4402   int i;
4403
4404   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
4405     return;
4406
4407   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
4408     MapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
4409
4410   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
4411     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
4412 }
4413
4414 static void HandleScreenGadgets(struct GadgetInfo *gi)
4415 {
4416   int id = gi->custom_id;
4417   int button = gi->event.button;
4418   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4419
4420 #if 0
4421   if (game_status != GAME_MODE_LEVELS && game_status != GAME_MODE_SETUP)
4422     return;
4423 #endif
4424
4425   switch (id)
4426   {
4427     case SCREEN_CTRL_ID_LAST_LEVEL:
4428       HandleMainMenu_SelectLevel(step, -1);
4429       break;
4430
4431     case SCREEN_CTRL_ID_NEXT_LEVEL:
4432       HandleMainMenu_SelectLevel(step, +1);
4433       break;
4434
4435     case SCREEN_CTRL_ID_LAST_PLAYER:
4436       HandleSetupScreen_Input_Player(step, -1);
4437       break;
4438
4439     case SCREEN_CTRL_ID_NEXT_PLAYER:
4440       HandleSetupScreen_Input_Player(step, +1);
4441       break;
4442
4443     case SCREEN_CTRL_ID_SCROLL_UP:
4444       if (game_status == GAME_MODE_LEVELS)
4445         HandleChooseLevel(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
4446       else if (game_status == GAME_MODE_SETUP)
4447         HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
4448       break;
4449
4450     case SCREEN_CTRL_ID_SCROLL_DOWN:
4451       if (game_status == GAME_MODE_LEVELS)
4452         HandleChooseLevel(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4453       else if (game_status == GAME_MODE_SETUP)
4454         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4455       break;
4456
4457     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
4458       if (game_status == GAME_MODE_LEVELS)
4459         HandleChooseLevel(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4460       else if (game_status == GAME_MODE_SETUP)
4461         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4462       break;
4463
4464     default:
4465       break;
4466   }
4467 }