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