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