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