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