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