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