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