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