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