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