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