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