rnd-20040621-1-src
[rocksndiamonds.git] / src / screens.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * screens.c                                                *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "screens.h"
17 #include "events.h"
18 #include "game.h"
19 #include "tools.h"
20 #include "editor.h"
21 #include "files.h"
22 #include "tape.h"
23 #include "cartoons.h"
24 #include "network.h"
25 #include "init.h"
26
27 /* screens in the setup menu */
28 #define SETUP_MODE_MAIN                 0
29 #define SETUP_MODE_GAME                 1
30 #define SETUP_MODE_EDITOR               2
31 #define SETUP_MODE_INPUT                3
32 #define SETUP_MODE_SHORTCUT             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(0);
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(NETWORK_AVALIABLE)
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(0);
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 #if 0
1956   { TYPE_STRING,        NULL,                   "Offer Special Elements:"},
1957 #endif
1958   { TYPE_SWITCH,        &setup.editor.el_boulderdash,   "BoulderDash:"  },
1959   { TYPE_SWITCH,        &setup.editor.el_emerald_mine,  "Emerald Mine:" },
1960   { TYPE_SWITCH,        &setup.editor.el_more,          "More:"         },
1961   { TYPE_SWITCH,        &setup.editor.el_sokoban,       "Sokoban:"      },
1962   { TYPE_SWITCH,        &setup.editor.el_supaplex,      "Supaplex:"     },
1963   { TYPE_SWITCH,        &setup.editor.el_diamond_caves, "Diamd. Caves:" },
1964   { TYPE_SWITCH,        &setup.editor.el_dx_boulderdash,"DX Boulderd.:" },
1965   { TYPE_SWITCH,        &setup.editor.el_chars,         "Characters:"   },
1966   { TYPE_SWITCH,        &setup.editor.el_custom,        "Custom:"       },
1967   { TYPE_SWITCH,        &setup.editor.el_custom_more,   "More Custom:"  },
1968   { TYPE_SWITCH,        &setup.editor.el_headlines,     "Headlines:"    },
1969   { TYPE_SWITCH,        &setup.editor.el_user_defined,  "User defined:" },
1970   { TYPE_EMPTY,         NULL,                   ""                      },
1971   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
1972
1973   { 0,                  NULL,                   NULL                    }
1974 };
1975
1976 static struct TokenInfo setup_info_graphics[] =
1977 {
1978   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen:"           },
1979   { TYPE_SWITCH,        &setup.scroll_delay,    "Scroll Delay:"         },
1980   { TYPE_SWITCH,        &setup.soft_scrolling,  "Soft Scroll.:"         },
1981 #if 0
1982   { TYPE_SWITCH,        &setup.double_buffering,"Buffered gfx:"         },
1983   { TYPE_SWITCH,        &setup.fading,          "Fading:"               },
1984 #endif
1985   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Doors:"          },
1986   { TYPE_SWITCH,        &setup.toons,           "Toons:"                },
1987   { TYPE_EMPTY,         NULL,                   ""                      },
1988   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
1989
1990   { 0,                  NULL,                   NULL                    }
1991 };
1992
1993 static struct TokenInfo setup_info_sound[] =
1994 {
1995   { TYPE_SWITCH,        &setup.sound_simple,    "Simple Sound:"         },
1996   { TYPE_SWITCH,        &setup.sound_loops,     "Sound Loops:"          },
1997   { TYPE_SWITCH,        &setup.sound_music,     "Game Music:"           },
1998   { TYPE_EMPTY,         NULL,                   ""                      },
1999   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2000
2001   { 0,                  NULL,                   NULL                    }
2002 };
2003
2004 static struct TokenInfo setup_info_artwork[] =
2005 {
2006   { TYPE_ENTER_MENU,    execSetupChooseGraphics,"Custom Graphics"       },
2007   { TYPE_STRING,        &graphics_set_name,     ""                      },
2008   { TYPE_ENTER_MENU,    execSetupChooseSounds,  "Custom Sounds"         },
2009   { TYPE_STRING,        &sounds_set_name,       ""                      },
2010   { TYPE_ENTER_MENU,    execSetupChooseMusic,   "Custom Music"          },
2011   { TYPE_STRING,        &music_set_name,        ""                      },
2012   { TYPE_EMPTY,         NULL,                   ""                      },
2013   { TYPE_STRING,        NULL,                   "Override Level Artwork:"},
2014   { TYPE_YES_NO,        &setup.override_level_graphics, "Graphics:"     },
2015   { TYPE_YES_NO,        &setup.override_level_sounds,   "Sounds:"       },
2016   { TYPE_YES_NO,        &setup.override_level_music,    "Music:"        },
2017   { TYPE_EMPTY,         NULL,                   ""                      },
2018   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2019
2020   { 0,                  NULL,                   NULL                    }
2021 };
2022
2023 static struct TokenInfo setup_info_shortcut[] =
2024 {
2025   { TYPE_KEYTEXT,       NULL,                   "Quick Save Game:",     },
2026   { TYPE_KEY,           &setup.shortcut.save_game,      ""              },
2027   { TYPE_KEYTEXT,       NULL,                   "Quick Load Game:",     },
2028   { TYPE_KEY,           &setup.shortcut.load_game,      ""              },
2029   { TYPE_KEYTEXT,       NULL,                   "Toggle Pause:",        },
2030   { TYPE_KEY,           &setup.shortcut.toggle_pause,   ""              },
2031   { TYPE_EMPTY,         NULL,                   ""                      },
2032   { TYPE_YES_NO,        &setup.ask_on_escape,   "Ask on Esc:"           },
2033   { TYPE_EMPTY,         NULL,                   ""                      },
2034   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
2035
2036   { 0,                  NULL,                   NULL                    }
2037 };
2038
2039 static Key getSetupKey()
2040 {
2041   Key key = KSYM_UNDEFINED;
2042   boolean got_key_event = FALSE;
2043
2044   while (!got_key_event)
2045   {
2046     if (PendingEvent())         /* got event */
2047     {
2048       Event event;
2049
2050       NextEvent(&event);
2051
2052       switch(event.type)
2053       {
2054         case EVENT_KEYPRESS:
2055           {
2056             key = GetEventKey((KeyEvent *)&event, TRUE);
2057
2058             /* press 'Escape' or 'Enter' to keep the existing key binding */
2059             if (key == KSYM_Escape || key == KSYM_Return)
2060               key = KSYM_UNDEFINED;     /* keep old value */
2061
2062             got_key_event = TRUE;
2063           }
2064           break;
2065
2066         case EVENT_KEYRELEASE:
2067           key_joystick_mapping = 0;
2068           break;
2069
2070         default:
2071           HandleOtherEvents(&event);
2072           break;
2073       }
2074     }
2075
2076     DoAnimation();
2077     BackToFront();
2078
2079     /* don't eat all CPU time */
2080     Delay(10);
2081   }
2082
2083   return key;
2084 }
2085
2086 static void drawSetupValue(int pos)
2087 {
2088   int xpos = MENU_SCREEN_VALUE_XPOS;
2089   int ypos = MENU_SCREEN_START_YPOS + pos;
2090   int font_nr = FONT_VALUE_1;
2091   int type = setup_info[pos].type;
2092   void *value = setup_info[pos].value;
2093   char *value_string = (!(type & TYPE_GHOSTED) ? getSetupValue(type, value) :
2094                         "n/a");
2095
2096   if (value_string == NULL)
2097     return;
2098
2099   if (type & TYPE_KEY)
2100   {
2101     xpos = 3;
2102
2103     if (type & TYPE_QUERY)
2104     {
2105       value_string = "<press key>";
2106       font_nr = FONT_INPUT_1_ACTIVE;
2107     }
2108   }
2109   else if (type & TYPE_STRING)
2110   {
2111     int max_value_len = (SCR_FIELDX - 2) * 2;
2112
2113     xpos = 1;
2114     font_nr = FONT_VALUE_2;
2115
2116     if (strlen(value_string) > max_value_len)
2117       value_string[max_value_len] = '\0';
2118   }
2119   else if (type & TYPE_BOOLEAN_STYLE)
2120   {
2121     font_nr = (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
2122   }
2123
2124   DrawText(mSX + xpos * 32, mSY + ypos * 32,
2125            (xpos == 3 ? "              " : "   "), font_nr);
2126   DrawText(mSX + xpos * 32, mSY + ypos * 32, value_string, font_nr);
2127 }
2128
2129 static void changeSetupValue(int pos)
2130 {
2131   if (setup_info[pos].type & TYPE_BOOLEAN_STYLE)
2132   {
2133     *(boolean *)setup_info[pos].value ^= TRUE;
2134   }
2135   else if (setup_info[pos].type & TYPE_KEY)
2136   {
2137     Key key;
2138
2139     setup_info[pos].type |= TYPE_QUERY;
2140     drawSetupValue(pos);
2141     setup_info[pos].type &= ~TYPE_QUERY;
2142
2143     key = getSetupKey();
2144     if (key != KSYM_UNDEFINED)
2145       *(Key *)setup_info[pos].value = key;
2146   }
2147
2148   drawSetupValue(pos);
2149 }
2150
2151 static void DrawSetupScreen_Generic()
2152 {
2153   char *title_string = NULL;
2154   int i;
2155
2156   UnmapAllGadgets();
2157   CloseDoor(DOOR_CLOSE_2);
2158
2159   ClearWindow();
2160
2161   if (setup_mode == SETUP_MODE_MAIN)
2162   {
2163     setup_info = setup_info_main;
2164     title_string = "Setup";
2165   }
2166   else if (setup_mode == SETUP_MODE_GAME)
2167   {
2168     setup_info = setup_info_game;
2169     title_string = "Setup Game";
2170   }
2171   else if (setup_mode == SETUP_MODE_EDITOR)
2172   {
2173     setup_info = setup_info_editor;
2174     title_string = "Setup Editor";
2175   }
2176   else if (setup_mode == SETUP_MODE_GRAPHICS)
2177   {
2178     setup_info = setup_info_graphics;
2179     title_string = "Setup Graphics";
2180   }
2181   else if (setup_mode == SETUP_MODE_SOUND)
2182   {
2183     setup_info = setup_info_sound;
2184     title_string = "Setup Sound";
2185   }
2186   else if (setup_mode == SETUP_MODE_ARTWORK)
2187   {
2188     setup_info = setup_info_artwork;
2189     title_string = "Custom Artwork";
2190   }
2191   else if (setup_mode == SETUP_MODE_SHORTCUT)
2192   {
2193     setup_info = setup_info_shortcut;
2194     title_string = "Setup Shortcuts";
2195   }
2196
2197   DrawText(mSX + 16, mSY + 16, title_string, FONT_TITLE_1);
2198
2199   num_setup_info = 0;
2200   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
2201   {
2202     void *value_ptr = setup_info[i].value;
2203     int ypos = MENU_SCREEN_START_YPOS + i;
2204     int font_nr = FONT_MENU_1;
2205
2206     /* set some entries to "unchangeable" according to other variables */
2207     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
2208         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
2209         (value_ptr == &setup.sound_music  && !audio.music_available) ||
2210         (value_ptr == &setup.fullscreen   && !video.fullscreen_available))
2211       setup_info[i].type |= TYPE_GHOSTED;
2212
2213 #if 0
2214     if (setup_info[i].type & TYPE_STRING ||
2215         (setup_info[i].type & TYPE_SWITCH && setup_mode == SETUP_MODE_EDITOR))
2216       font_nr = FONT_MENU_2;
2217 #else
2218     if (setup_info[i].type & TYPE_STRING)
2219       font_nr = FONT_MENU_2;
2220 #endif
2221
2222     DrawText(mSX + 32, mSY + ypos * 32, setup_info[i].text, font_nr);
2223
2224     if (setup_info[i].type & TYPE_ENTER_MENU)
2225       initCursor(i, IMG_MENU_BUTTON_RIGHT);
2226     else if (setup_info[i].type & TYPE_LEAVE_MENU)
2227       initCursor(i, IMG_MENU_BUTTON_LEFT);
2228     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
2229       initCursor(i, IMG_MENU_BUTTON);
2230
2231     if (setup_info[i].type & TYPE_VALUE)
2232       drawSetupValue(i);
2233
2234     num_setup_info++;
2235   }
2236
2237   FadeToFront();
2238   InitAnimation();
2239   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
2240 }
2241
2242 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
2243 {
2244   static int choice_store[MAX_SETUP_MODES];
2245   int choice = choice_store[setup_mode];        /* always starts with 0 */
2246   int x = 0;
2247   int y = choice;
2248
2249   if (button == MB_MENU_INITIALIZE)
2250   {
2251     /* advance to first valid menu entry */
2252     while (choice < num_setup_info &&
2253            setup_info[choice].type & TYPE_SKIP_ENTRY)
2254       choice++;
2255     choice_store[setup_mode] = choice;
2256
2257     drawCursor(choice, FC_RED);
2258     return;
2259   }
2260   else if (button == MB_MENU_LEAVE)
2261   {
2262     for (y = 0; y < num_setup_info; y++)
2263     {
2264       if (setup_info[y].type & TYPE_LEAVE_MENU)
2265       {
2266         void (*menu_callback_function)(void) = setup_info[y].value;
2267
2268         menu_callback_function();
2269         break;  /* absolutely needed because function changes 'setup_info'! */
2270       }
2271     }
2272
2273     return;
2274   }
2275
2276   if (mx || my)         /* mouse input */
2277   {
2278     x = (mx - mSX) / 32;
2279     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
2280   }
2281   else if (dx || dy)    /* keyboard input */
2282   {
2283     if (dx)
2284     {
2285       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE_MENU : TYPE_ENTER_MENU);
2286
2287       if (setup_info[choice].type & menu_navigation_type ||
2288           setup_info[choice].type & TYPE_BOOLEAN_STYLE)
2289         button = MB_MENU_CHOICE;
2290     }
2291     else if (dy)
2292       y = choice + dy;
2293
2294     /* jump to next non-empty menu entry (up or down) */
2295     while (y > 0 && y < num_setup_info - 1 &&
2296            setup_info[y].type & TYPE_SKIP_ENTRY)
2297       y += dy;
2298   }
2299
2300   if (IN_VIS_FIELD(x, y) &&
2301       y >= 0 && y < num_setup_info && setup_info[y].type & ~TYPE_SKIP_ENTRY)
2302   {
2303     if (button)
2304     {
2305       if (y != choice)
2306       {
2307         drawCursor(y, FC_RED);
2308         drawCursor(choice, FC_BLUE);
2309         choice = choice_store[setup_mode] = y;
2310       }
2311     }
2312     else if (!(setup_info[y].type & TYPE_GHOSTED))
2313     {
2314       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE_MENU)
2315       {
2316         void (*menu_callback_function)(void) = setup_info[choice].value;
2317
2318         menu_callback_function();
2319       }
2320       else
2321       {
2322         if (setup_info[y].type & TYPE_KEYTEXT &&
2323             setup_info[y + 1].type & TYPE_KEY)
2324           y++;
2325
2326         if (setup_info[y].type & TYPE_VALUE)
2327           changeSetupValue(y);
2328       }
2329     }
2330   }
2331 }
2332
2333 void DrawSetupScreen_Input()
2334 {
2335   ClearWindow();
2336
2337   DrawText(mSX+16, mSY+16, "Setup Input", FONT_TITLE_1);
2338
2339   initCursor(0, IMG_MENU_BUTTON);
2340   initCursor(1, IMG_MENU_BUTTON);
2341   initCursor(2, IMG_MENU_BUTTON_RIGHT);
2342   initCursor(13, IMG_MENU_BUTTON_LEFT);
2343
2344   drawCursorXY(10, 0, IMG_MENU_BUTTON_LEFT);
2345   drawCursorXY(12, 0, IMG_MENU_BUTTON_RIGHT);
2346
2347   DrawText(mSX+32, mSY+2*32, "Player:", FONT_MENU_1);
2348   DrawText(mSX+32, mSY+3*32, "Device:", FONT_MENU_1);
2349   DrawText(mSX+32, mSY+15*32, "Back",   FONT_MENU_1);
2350
2351 #if 0
2352   DeactivateJoystickForCalibration();
2353   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
2354                     "Joysticks deactivated on this screen");
2355 #endif
2356
2357   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
2358   FadeToFront();
2359   InitAnimation();
2360 }
2361
2362 static void setJoystickDeviceToNr(char *device_name, int device_nr)
2363 {
2364   if (device_name == NULL)
2365     return;
2366
2367   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
2368     device_nr = 0;
2369
2370   if (strlen(device_name) > 1)
2371   {
2372     char c1 = device_name[strlen(device_name) - 1];
2373     char c2 = device_name[strlen(device_name) - 2];
2374
2375     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
2376       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
2377   }
2378   else
2379     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
2380             strlen(device_name));
2381 }
2382
2383 static void drawPlayerSetupInputInfo(int player_nr)
2384 {
2385   int i;
2386   static struct SetupKeyboardInfo custom_key;
2387   static struct
2388   {
2389     Key *key;
2390     char *text;
2391   } custom[] =
2392   {
2393     { &custom_key.left,  "Joystick Left"  },
2394     { &custom_key.right, "Joystick Right" },
2395     { &custom_key.up,    "Joystick Up"    },
2396     { &custom_key.down,  "Joystick Down"  },
2397     { &custom_key.snap,  "Button 1"       },
2398     { &custom_key.drop,  "Button 2"       }
2399   };
2400   static char *joystick_name[MAX_PLAYERS] =
2401   {
2402     "Joystick1",
2403     "Joystick2",
2404     "Joystick3",
2405     "Joystick4"
2406   };
2407
2408   custom_key = setup.input[player_nr].key;
2409
2410   DrawText(mSX+11*32, mSY+2*32, int2str(player_nr +1, 1), FONT_INPUT_1_ACTIVE);
2411 #if 1
2412   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
2413                              TILEX, TILEY);
2414   DrawGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
2415                          PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
2416 #else
2417   DrawGraphicThruMask(8, 2, PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
2418 #endif
2419
2420   if (setup.input[player_nr].use_joystick)
2421   {
2422     char *device_name = setup.input[player_nr].joy.device_name;
2423
2424     DrawText(mSX+8*32, mSY+3*32,
2425              joystick_name[getJoystickNrFromDeviceName(device_name)],
2426              FONT_VALUE_1);
2427     DrawText(mSX+32, mSY+4*32, "Calibrate", FONT_MENU_1);
2428   }
2429   else
2430   {
2431     DrawText(mSX+8*32, mSY+3*32, "Keyboard ", FONT_VALUE_1);
2432     DrawText(mSX+32,   mSY+4*32, "Customize", FONT_MENU_1);
2433   }
2434
2435   DrawText(mSX+32, mSY+5*32, "Actual Settings:", FONT_MENU_1);
2436   drawCursorXY(1, 4, IMG_MENU_BUTTON_LEFT);
2437   drawCursorXY(1, 5, IMG_MENU_BUTTON_RIGHT);
2438   drawCursorXY(1, 6, IMG_MENU_BUTTON_UP);
2439   drawCursorXY(1, 7, IMG_MENU_BUTTON_DOWN);
2440   DrawText(mSX+2*32, mSY+6*32, ":", FONT_VALUE_OLD);
2441   DrawText(mSX+2*32, mSY+7*32, ":", FONT_VALUE_OLD);
2442   DrawText(mSX+2*32, mSY+8*32, ":", FONT_VALUE_OLD);
2443   DrawText(mSX+2*32, mSY+9*32, ":", FONT_VALUE_OLD);
2444   DrawText(mSX+32, mSY+10*32, "Snap Field:", FONT_VALUE_OLD);
2445   DrawText(mSX+32, mSY+12*32, "Drop Element:", FONT_VALUE_OLD);
2446
2447   for (i = 0; i < 6; i++)
2448   {
2449     int ypos = 6 + i + (i > 3 ? i-3 : 0);
2450
2451     DrawText(mSX + 3*32, mSY + ypos*32,
2452              "              ", FONT_VALUE_1);
2453     DrawText(mSX + 3*32, mSY + ypos*32,
2454              (setup.input[player_nr].use_joystick ?
2455               custom[i].text :
2456               getKeyNameFromKey(*custom[i].key)), FONT_VALUE_1);
2457   }
2458 }
2459
2460 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
2461 {
2462   static int choice = 0;
2463   static int player_nr = 0;
2464   int x = 0;
2465   int y = choice;
2466   int pos_start  = SETUPINPUT_SCREEN_POS_START;
2467   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
2468   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
2469   int pos_end    = SETUPINPUT_SCREEN_POS_END;
2470
2471   if (button == MB_MENU_INITIALIZE)
2472   {
2473     drawPlayerSetupInputInfo(player_nr);
2474     drawCursor(choice, FC_RED);
2475
2476     return;
2477   }
2478   else if (button == MB_MENU_LEAVE)
2479   {
2480     setup_mode = SETUP_MODE_MAIN;
2481     DrawSetupScreen();
2482     InitJoysticks();
2483
2484     return;
2485   }
2486
2487   if (mx || my)         /* mouse input */
2488   {
2489     x = (mx - mSX) / 32;
2490     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
2491   }
2492   else if (dx || dy)    /* keyboard input */
2493   {
2494     if (dx && choice == 0)
2495       x = (dx < 0 ? 10 : 12);
2496     else if ((dx && choice == 1) ||
2497              (dx == +1 && choice == 2) ||
2498              (dx == -1 && choice == pos_end))
2499       button = MB_MENU_CHOICE;
2500     else if (dy)
2501       y = choice + dy;
2502
2503     if (y >= pos_empty1 && y <= pos_empty2)
2504       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
2505   }
2506
2507   if (IN_VIS_FIELD(x, y) &&
2508       y == 0 && ((x < 10 && !button) || ((x == 10 || x == 12) && button)))
2509   {
2510     static unsigned long delay = 0;
2511
2512     if (!DelayReached(&delay, GADGET_FRAME_DELAY))
2513       return;
2514
2515     player_nr = (player_nr + (x == 10 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
2516
2517     drawPlayerSetupInputInfo(player_nr);
2518   }
2519   else if (IN_VIS_FIELD(x, y) &&
2520            y >= pos_start && y <= pos_end &&
2521            !(y >= pos_empty1 && y <= pos_empty2))
2522   {
2523     if (button)
2524     {
2525       if (y != choice)
2526       {
2527         drawCursor(y, FC_RED);
2528         drawCursor(choice, FC_BLUE);
2529         choice = y;
2530       }
2531     }
2532     else
2533     {
2534       if (y == 1)
2535       {
2536         char *device_name = setup.input[player_nr].joy.device_name;
2537
2538         if (!setup.input[player_nr].use_joystick)
2539         {
2540           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
2541
2542           setJoystickDeviceToNr(device_name, new_device_nr);
2543           setup.input[player_nr].use_joystick = TRUE;
2544         }
2545         else
2546         {
2547           int device_nr = getJoystickNrFromDeviceName(device_name);
2548           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
2549
2550           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
2551             setup.input[player_nr].use_joystick = FALSE;
2552           else
2553             setJoystickDeviceToNr(device_name, new_device_nr);
2554         }
2555
2556         drawPlayerSetupInputInfo(player_nr);
2557       }
2558       else if (y == 2)
2559       {
2560         if (setup.input[player_nr].use_joystick)
2561         {
2562           InitJoysticks();
2563           CalibrateJoystick(player_nr);
2564         }
2565         else
2566           CustomizeKeyboard(player_nr);
2567       }
2568       else if (y == pos_end)
2569       {
2570         InitJoysticks();
2571
2572         setup_mode = SETUP_MODE_MAIN;
2573         DrawSetupScreen();
2574       }
2575     }
2576   }
2577 }
2578
2579 void CustomizeKeyboard(int player_nr)
2580 {
2581   int i;
2582   int step_nr;
2583   boolean finished = FALSE;
2584   static struct SetupKeyboardInfo custom_key;
2585   static struct
2586   {
2587     Key *key;
2588     char *text;
2589   } customize_step[] =
2590   {
2591     { &custom_key.left,  "Move Left"    },
2592     { &custom_key.right, "Move Right"   },
2593     { &custom_key.up,    "Move Up"      },
2594     { &custom_key.down,  "Move Down"    },
2595     { &custom_key.snap,  "Snap Field"   },
2596     { &custom_key.drop,  "Drop Element" }
2597   };
2598
2599   /* read existing key bindings from player setup */
2600   custom_key = setup.input[player_nr].key;
2601
2602   ClearWindow();
2603   DrawText(mSX + 16, mSY + 16, "Keyboard Input", FONT_TITLE_1);
2604
2605   BackToFront();
2606   InitAnimation();
2607
2608   step_nr = 0;
2609   DrawText(mSX, mSY + (2+2*step_nr)*32,
2610            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
2611   DrawText(mSX, mSY + (2+2*step_nr+1)*32,
2612            "Key:", FONT_INPUT_1_ACTIVE);
2613   DrawText(mSX + 4*32, mSY + (2+2*step_nr+1)*32,
2614            getKeyNameFromKey(*customize_step[step_nr].key), FONT_VALUE_OLD);
2615
2616   while (!finished)
2617   {
2618     if (PendingEvent())         /* got event */
2619     {
2620       Event event;
2621
2622       NextEvent(&event);
2623
2624       switch(event.type)
2625       {
2626         case EVENT_KEYPRESS:
2627           {
2628             Key key = GetEventKey((KeyEvent *)&event, FALSE);
2629
2630             if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
2631             {
2632               finished = TRUE;
2633               break;
2634             }
2635
2636             /* all keys configured -- wait for "Escape" or "Return" key */
2637             if (step_nr == 6)
2638               break;
2639
2640             /* press 'Enter' to keep the existing key binding */
2641             if (key == KSYM_Return)
2642               key = *customize_step[step_nr].key;
2643
2644             /* check if key already used */
2645             for (i = 0; i < step_nr; i++)
2646               if (*customize_step[i].key == key)
2647                 break;
2648             if (i < step_nr)
2649               break;
2650
2651             /* got new key binding */
2652             *customize_step[step_nr].key = key;
2653             DrawText(mSX + 4*32, mSY + (2+2*step_nr+1)*32,
2654                      "             ", FONT_VALUE_1);
2655             DrawText(mSX + 4*32, mSY + (2+2*step_nr+1)*32,
2656                      getKeyNameFromKey(key), FONT_VALUE_1);
2657             step_nr++;
2658
2659             /* un-highlight last query */
2660             DrawText(mSX, mSY+(2+2*(step_nr-1))*32,
2661                      customize_step[step_nr-1].text, FONT_MENU_1);
2662             DrawText(mSX, mSY+(2+2*(step_nr-1)+1)*32,
2663                      "Key:", FONT_MENU_1);
2664
2665             /* press 'Enter' to leave */
2666             if (step_nr == 6)
2667             {
2668               DrawText(mSX + 16, mSY + 15*32+16,
2669                        "Press Enter", FONT_TITLE_1);
2670               break;
2671             }
2672
2673             /* query next key binding */
2674             DrawText(mSX, mSY+(2+2*step_nr)*32,
2675                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
2676             DrawText(mSX, mSY+(2+2*step_nr+1)*32,
2677                      "Key:", FONT_INPUT_1_ACTIVE);
2678             DrawText(mSX + 4*32, mSY+(2+2*step_nr+1)*32,
2679                      getKeyNameFromKey(*customize_step[step_nr].key),
2680                      FONT_VALUE_OLD);
2681           }
2682           break;
2683
2684         case EVENT_KEYRELEASE:
2685           key_joystick_mapping = 0;
2686           break;
2687
2688         default:
2689           HandleOtherEvents(&event);
2690           break;
2691       }
2692     }
2693
2694     DoAnimation();
2695     BackToFront();
2696
2697     /* don't eat all CPU time */
2698     Delay(10);
2699   }
2700
2701   /* write new key bindings back to player setup */
2702   setup.input[player_nr].key = custom_key;
2703
2704   StopAnimation();
2705   DrawSetupScreen_Input();
2706 }
2707
2708 static boolean CalibrateJoystickMain(int player_nr)
2709 {
2710   int new_joystick_xleft = JOYSTICK_XMIDDLE;
2711   int new_joystick_xright = JOYSTICK_XMIDDLE;
2712   int new_joystick_yupper = JOYSTICK_YMIDDLE;
2713   int new_joystick_ylower = JOYSTICK_YMIDDLE;
2714   int new_joystick_xmiddle, new_joystick_ymiddle;
2715
2716   int joystick_fd = joystick.fd[player_nr];
2717   int x, y, last_x, last_y, xpos = 8, ypos = 3;
2718   boolean check[3][3];
2719   int check_remaining = 3 * 3;
2720   int joy_x, joy_y;
2721   int joy_value;
2722   int result = -1;
2723
2724   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
2725     return FALSE;
2726
2727   if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
2728     return FALSE;
2729
2730   ClearWindow();
2731
2732   for (y = 0; y < 3; y++)
2733   {
2734     for (x = 0; x < 3; x++)
2735     {
2736       DrawGraphic(xpos + x - 1, ypos + y - 1, IMG_MENU_CALIBRATE_BLUE, 0);
2737       check[x][y] = FALSE;
2738     }
2739   }
2740
2741   DrawText(mSX,      mSY +  6 * 32, " ROTATE JOYSTICK ", FONT_TITLE_1);
2742   DrawText(mSX,      mSY +  7 * 32, "IN ALL DIRECTIONS", FONT_TITLE_1);
2743   DrawText(mSX + 16, mSY +  9 * 32, "  IF ALL BALLS  ",  FONT_TITLE_1);
2744   DrawText(mSX,      mSY + 10 * 32, "   ARE YELLOW,   ", FONT_TITLE_1);
2745   DrawText(mSX,      mSY + 11 * 32, " CENTER JOYSTICK ", FONT_TITLE_1);
2746   DrawText(mSX,      mSY + 12 * 32, "       AND       ", FONT_TITLE_1);
2747   DrawText(mSX,      mSY + 13 * 32, "PRESS ANY BUTTON!", FONT_TITLE_1);
2748
2749   joy_value = Joystick(player_nr);
2750   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
2751   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
2752
2753   /* eventually uncalibrated center position (joystick could be uncentered) */
2754   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
2755     return FALSE;
2756
2757   new_joystick_xmiddle = joy_x;
2758   new_joystick_ymiddle = joy_y;
2759
2760   DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_RED, 0);
2761   BackToFront();
2762
2763   while (Joystick(player_nr) & JOY_BUTTON);     /* wait for released button */
2764   InitAnimation();
2765
2766   while (result < 0)
2767   {
2768     if (PendingEvent())         /* got event */
2769     {
2770       Event event;
2771
2772       NextEvent(&event);
2773
2774       switch(event.type)
2775       {
2776         case EVENT_KEYPRESS:
2777           switch(GetEventKey((KeyEvent *)&event, TRUE))
2778           {
2779             case KSYM_Return:
2780               if (check_remaining == 0)
2781                 result = 1;
2782               break;
2783
2784             case KSYM_Escape:
2785               result = 0;
2786               break;
2787
2788             default:
2789               break;
2790           }
2791           break;
2792
2793         case EVENT_KEYRELEASE:
2794           key_joystick_mapping = 0;
2795           break;
2796
2797         default:
2798           HandleOtherEvents(&event);
2799           break;
2800       }
2801     }
2802
2803     if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
2804       return FALSE;
2805
2806     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
2807     new_joystick_xright = MAX(new_joystick_xright, joy_x);
2808     new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
2809     new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
2810
2811     setup.input[player_nr].joy.xleft = new_joystick_xleft;
2812     setup.input[player_nr].joy.yupper = new_joystick_yupper;
2813     setup.input[player_nr].joy.xright = new_joystick_xright;
2814     setup.input[player_nr].joy.ylower = new_joystick_ylower;
2815     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
2816     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
2817
2818     CheckJoystickData();
2819
2820     joy_value = Joystick(player_nr);
2821
2822     if (joy_value & JOY_BUTTON && check_remaining == 0)
2823       result = 1;
2824
2825     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
2826     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
2827
2828     if (x != last_x || y != last_y)
2829     {
2830       DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_YELLOW, 0);
2831       DrawGraphic(xpos + x,      ypos + y,      IMG_MENU_CALIBRATE_RED,    0);
2832
2833       last_x = x;
2834       last_y = y;
2835
2836       if (check_remaining > 0 && !check[x+1][y+1])
2837       {
2838         check[x+1][y+1] = TRUE;
2839         check_remaining--;
2840       }
2841
2842 #if 0
2843 #ifdef DEBUG
2844       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
2845              setup.input[player_nr].joy.xleft,
2846              setup.input[player_nr].joy.xmiddle,
2847              setup.input[player_nr].joy.xright);
2848       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
2849              setup.input[player_nr].joy.yupper,
2850              setup.input[player_nr].joy.ymiddle,
2851              setup.input[player_nr].joy.ylower);
2852 #endif
2853 #endif
2854
2855     }
2856
2857     DoAnimation();
2858     BackToFront();
2859
2860     /* don't eat all CPU time */
2861     Delay(10);
2862   }
2863
2864   /* calibrated center position (joystick should now be centered) */
2865   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
2866     return FALSE;
2867
2868   new_joystick_xmiddle = joy_x;
2869   new_joystick_ymiddle = joy_y;
2870
2871   StopAnimation();
2872
2873   DrawSetupScreen_Input();
2874
2875   /* wait until the last pressed button was released */
2876   while (Joystick(player_nr) & JOY_BUTTON)
2877   {
2878     if (PendingEvent())         /* got event */
2879     {
2880       Event event;
2881
2882       NextEvent(&event);
2883       HandleOtherEvents(&event);
2884
2885       Delay(10);
2886     }
2887   }
2888
2889   return TRUE;
2890 }
2891
2892 void CalibrateJoystick(int player_nr)
2893 {
2894   if (!CalibrateJoystickMain(player_nr))
2895   {
2896     ClearWindow();
2897
2898     DrawText(mSX + 16, mSY + 6 * 32, "  JOYSTICK NOT  ",  FONT_TITLE_1);
2899     DrawText(mSX,      mSY + 7 * 32, "    AVAILABLE    ", FONT_TITLE_1);
2900     BackToFront();
2901     Delay(2000);        /* show error message for two seconds */
2902   }
2903 }
2904
2905 void DrawSetupScreen()
2906 {
2907   DeactivateJoystick();
2908
2909   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
2910
2911   if (setup_mode == SETUP_MODE_INPUT)
2912     DrawSetupScreen_Input();
2913   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
2914     DrawChooseTree(&artwork.gfx_current);
2915   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
2916     DrawChooseTree(&artwork.snd_current);
2917   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
2918     DrawChooseTree(&artwork.mus_current);
2919   else
2920     DrawSetupScreen_Generic();
2921
2922   PlayMenuSound();
2923   PlayMenuMusic();
2924 }
2925
2926 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
2927 {
2928   if (setup_mode == SETUP_MODE_INPUT)
2929     HandleSetupScreen_Input(mx, my, dx, dy, button);
2930   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
2931     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
2932   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
2933     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
2934   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
2935     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
2936   else
2937     HandleSetupScreen_Generic(mx, my, dx, dy, button);
2938
2939   DoAnimation();
2940 }
2941
2942 void HandleGameActions()
2943 {
2944   if (game_status != GAME_MODE_PLAYING)
2945     return;
2946
2947   if (local_player->LevelSolved)
2948     GameWon();
2949
2950   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
2951     TapeStop();
2952
2953   GameActions();
2954   BackToFront();
2955
2956   if (tape.auto_play && !tape.playing)
2957     AutoPlayTape();     /* continue automatically playing next tape */
2958 }
2959
2960 /* ---------- new screen button stuff -------------------------------------- */
2961
2962 /* graphic position and size values for buttons and scrollbars */
2963 #define SC_SCROLLBUTTON_XSIZE           TILEX
2964 #define SC_SCROLLBUTTON_YSIZE           TILEY
2965
2966 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
2967 #define SC_SCROLL_VERTICAL_YSIZE        ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \
2968                                          SC_SCROLLBUTTON_YSIZE)
2969 #define SC_SCROLL_UP_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
2970 #define SC_SCROLL_UP_YPOS               (2 * SC_SCROLLBUTTON_YSIZE)
2971 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLL_UP_XPOS
2972 #define SC_SCROLL_VERTICAL_YPOS         (SC_SCROLL_UP_YPOS + \
2973                                          SC_SCROLLBUTTON_YSIZE)
2974 #define SC_SCROLL_DOWN_XPOS             SC_SCROLL_UP_XPOS
2975 #define SC_SCROLL_DOWN_YPOS             (SC_SCROLL_VERTICAL_YPOS + \
2976                                          SC_SCROLL_VERTICAL_YSIZE)
2977
2978 #define SC_BORDER_SIZE                  14
2979
2980 static struct
2981 {
2982   int gfx_unpressed, gfx_pressed;
2983   int x, y;
2984   int gadget_id;
2985   char *infotext;
2986 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
2987 {
2988   {
2989     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
2990     SC_SCROLL_UP_XPOS, SC_SCROLL_UP_YPOS,
2991     SCREEN_CTRL_ID_SCROLL_UP,
2992     "scroll up"
2993   },
2994   {
2995     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
2996     SC_SCROLL_DOWN_XPOS, SC_SCROLL_DOWN_YPOS,
2997     SCREEN_CTRL_ID_SCROLL_DOWN,
2998     "scroll down"
2999   }
3000 };
3001
3002 static struct
3003 {
3004 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3005   Bitmap **gfx_unpressed, **gfx_pressed;
3006 #else
3007   int gfx_unpressed, gfx_pressed;
3008 #endif
3009   int x, y;
3010   int width, height;
3011   int type;
3012   int gadget_id;
3013   char *infotext;
3014 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
3015 {
3016   {
3017 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3018     &scrollbar_bitmap[0], &scrollbar_bitmap[1],
3019 #else
3020     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
3021 #endif
3022     SC_SCROLL_VERTICAL_XPOS, SC_SCROLL_VERTICAL_YPOS,
3023     SC_SCROLL_VERTICAL_XSIZE, SC_SCROLL_VERTICAL_YSIZE,
3024     GD_TYPE_SCROLLBAR_VERTICAL,
3025     SCREEN_CTRL_ID_SCROLL_VERTICAL,
3026     "scroll level series vertically"
3027   }
3028 };
3029
3030 static void CreateScreenScrollbuttons()
3031 {
3032   struct GadgetInfo *gi;
3033   unsigned long event_mask;
3034   int i;
3035
3036   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
3037   {
3038     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
3039     int gfx_unpressed, gfx_pressed;
3040     int x, y, width, height;
3041     int gd_x1, gd_x2, gd_y1, gd_y2;
3042     int id = scrollbutton_info[i].gadget_id;
3043
3044     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
3045
3046     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
3047     y = mSY + scrollbutton_info[i].y;
3048     width = SC_SCROLLBUTTON_XSIZE;
3049     height = SC_SCROLLBUTTON_YSIZE;
3050
3051     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
3052       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
3053                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
3054
3055     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
3056     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
3057     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
3058     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
3059     gd_x1 = graphic_info[gfx_unpressed].src_x;
3060     gd_y1 = graphic_info[gfx_unpressed].src_y;
3061     gd_x2 = graphic_info[gfx_pressed].src_x;
3062     gd_y2 = graphic_info[gfx_pressed].src_y;
3063
3064     gi = CreateGadget(GDI_CUSTOM_ID, id,
3065                       GDI_CUSTOM_TYPE_ID, i,
3066                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
3067                       GDI_X, x,
3068                       GDI_Y, y,
3069                       GDI_WIDTH, width,
3070                       GDI_HEIGHT, height,
3071                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
3072                       GDI_STATE, GD_BUTTON_UNPRESSED,
3073                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
3074                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
3075                       GDI_DIRECT_DRAW, FALSE,
3076                       GDI_EVENT_MASK, event_mask,
3077                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
3078                       GDI_END);
3079
3080     if (gi == NULL)
3081       Error(ERR_EXIT, "cannot create gadget");
3082
3083     screen_gadget[id] = gi;
3084   }
3085 }
3086
3087 static void CreateScreenScrollbars()
3088 {
3089   int i;
3090
3091   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
3092   {
3093     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
3094 #if !defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3095     int gfx_unpressed, gfx_pressed;
3096 #endif
3097     int x, y, width, height;
3098     int gd_x1, gd_x2, gd_y1, gd_y2;
3099     struct GadgetInfo *gi;
3100     int items_max, items_visible, item_position;
3101     unsigned long event_mask;
3102     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
3103     int id = scrollbar_info[i].gadget_id;
3104
3105     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
3106
3107     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
3108     y = mSY + scrollbar_info[i].y;
3109     width  = scrollbar_info[i].width;
3110     height = scrollbar_info[i].height;
3111
3112     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
3113       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
3114
3115     items_max = num_page_entries;
3116     items_visible = num_page_entries;
3117     item_position = 0;
3118
3119 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3120     gd_bitmap_unpressed = *scrollbar_info[i].gfx_unpressed;
3121     gd_bitmap_pressed   = *scrollbar_info[i].gfx_pressed;
3122     gd_x1 = 0;
3123     gd_y1 = 0;
3124     gd_x2 = 0;
3125     gd_y2 = 0;
3126 #else
3127     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
3128     gfx_pressed   = scrollbar_info[i].gfx_pressed;
3129     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
3130     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
3131     gd_x1 = graphic_info[gfx_unpressed].src_x;
3132     gd_y1 = graphic_info[gfx_unpressed].src_y;
3133     gd_x2 = graphic_info[gfx_pressed].src_x;
3134     gd_y2 = graphic_info[gfx_pressed].src_y;
3135 #endif
3136
3137     gi = CreateGadget(GDI_CUSTOM_ID, id,
3138                       GDI_CUSTOM_TYPE_ID, i,
3139                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
3140                       GDI_X, x,
3141                       GDI_Y, y,
3142                       GDI_WIDTH, width,
3143                       GDI_HEIGHT, height,
3144                       GDI_TYPE, scrollbar_info[i].type,
3145                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
3146                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
3147                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
3148                       GDI_STATE, GD_BUTTON_UNPRESSED,
3149                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
3150                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
3151                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
3152                       GDI_DIRECT_DRAW, FALSE,
3153                       GDI_EVENT_MASK, event_mask,
3154                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
3155                       GDI_END);
3156
3157     if (gi == NULL)
3158       Error(ERR_EXIT, "cannot create gadget");
3159
3160     screen_gadget[id] = gi;
3161   }
3162 }
3163
3164 void CreateScreenGadgets()
3165 {
3166   int last_game_status = game_status;   /* save current game status */
3167
3168 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3169   int i;
3170
3171   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
3172   {
3173     scrollbar_bitmap[i] = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
3174
3175     /* copy pointers to clip mask and GC */
3176     scrollbar_bitmap[i]->clip_mask =
3177       graphic_info[IMG_MENU_SCROLLBAR + i].clip_mask;
3178     scrollbar_bitmap[i]->stored_clip_gc =
3179       graphic_info[IMG_MENU_SCROLLBAR + i].clip_gc;
3180
3181     BlitBitmap(graphic_info[IMG_MENU_SCROLLBAR + i].bitmap,
3182                scrollbar_bitmap[i],
3183                graphic_info[IMG_MENU_SCROLLBAR + i].src_x,
3184                graphic_info[IMG_MENU_SCROLLBAR + i].src_y,
3185                TILEX, TILEY, 0, 0);
3186   }
3187 #endif
3188
3189   /* force LEVELS draw offset for scrollbar / scrollbutton gadgets */
3190   game_status = GAME_MODE_LEVELS;
3191
3192   CreateScreenScrollbuttons();
3193   CreateScreenScrollbars();
3194
3195   game_status = last_game_status;       /* restore current game status */
3196 }
3197
3198 void FreeScreenGadgets()
3199 {
3200   int i;
3201
3202 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
3203   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
3204   {
3205     /* prevent freeing clip mask and GC twice */
3206     scrollbar_bitmap[i]->clip_mask = None;
3207     scrollbar_bitmap[i]->stored_clip_gc = None;
3208
3209     FreeBitmap(scrollbar_bitmap[i]);
3210   }
3211 #endif
3212
3213   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
3214     FreeGadget(screen_gadget[i]);
3215 }
3216
3217 void MapChooseTreeGadgets(TreeInfo *ti)
3218 {
3219   int num_entries = numTreeInfoInGroup(ti);
3220   int i;
3221
3222   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
3223     return;
3224
3225   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
3226     MapGadget(screen_gadget[i]);
3227 }
3228
3229 #if 0
3230 void UnmapChooseTreeGadgets()
3231 {
3232   int i;
3233
3234   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
3235     UnmapGadget(screen_gadget[i]);
3236 }
3237 #endif
3238
3239 static void HandleScreenGadgets(struct GadgetInfo *gi)
3240 {
3241   int id = gi->custom_id;
3242
3243   if (game_status != GAME_MODE_LEVELS && game_status != GAME_MODE_SETUP)
3244     return;
3245
3246   switch (id)
3247   {
3248     case SCREEN_CTRL_ID_SCROLL_UP:
3249       if (game_status == GAME_MODE_LEVELS)
3250         HandleChooseLevel(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
3251       else if (game_status == GAME_MODE_SETUP)
3252         HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
3253       break;
3254
3255     case SCREEN_CTRL_ID_SCROLL_DOWN:
3256       if (game_status == GAME_MODE_LEVELS)
3257         HandleChooseLevel(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
3258       else if (game_status == GAME_MODE_SETUP)
3259         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
3260       break;
3261
3262     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
3263       if (game_status == GAME_MODE_LEVELS)
3264         HandleChooseLevel(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
3265       else if (game_status == GAME_MODE_SETUP)
3266         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
3267       break;
3268
3269     default:
3270       break;
3271   }
3272 }