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