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