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