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