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