rnd-20000815-1-src
[rocksndiamonds.git] / src / screens.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  screens.c                                               *
12 ***********************************************************/
13
14 #include "screens.h"
15 #include "events.h"
16 #include "sound.h"
17 #include "game.h"
18 #include "tools.h"
19 #include "editor.h"
20 #include "misc.h"
21 #include "files.h"
22 #include "buttons.h"
23 #include "tape.h"
24 #include "joystick.h"
25 #include "cartoons.h"
26 #include "network.h"
27 #include "init.h"
28
29 /* for DrawSetupScreen(), HandleSetupScreen() */
30 #define SETUP_SCREEN_POS_START          2
31 #define SETUP_SCREEN_POS_END            (SCR_FIELDY - 1)
32 #define SETUP_SCREEN_POS_EMPTY1         (SETUP_SCREEN_POS_END - 2)
33 #define SETUP_SCREEN_POS_EMPTY2         (SETUP_SCREEN_POS_END - 2)
34
35 /* for HandleSetupInputScreen() */
36 #define SETUPINPUT_SCREEN_POS_START     2
37 #define SETUPINPUT_SCREEN_POS_END       (SCR_FIELDY - 2)
38 #define SETUPINPUT_SCREEN_POS_EMPTY1    (SETUPINPUT_SCREEN_POS_START + 3)
39 #define SETUPINPUT_SCREEN_POS_EMPTY2    (SETUPINPUT_SCREEN_POS_END - 1)
40
41 /* for HandleChooseLevel() */
42 #define MAX_LEVEL_SERIES_ON_SCREEN      (SCR_FIELDY - 2)
43
44 /* buttons and scrollbars identifiers */
45 #define SCREEN_CTRL_ID_SCROLL_UP        0
46 #define SCREEN_CTRL_ID_SCROLL_DOWN      1
47 #define SCREEN_CTRL_ID_SCROLL_VERTICAL  2
48
49 #define NUM_SCREEN_SCROLLBUTTONS        2
50 #define NUM_SCREEN_SCROLLBARS           1
51 #define NUM_SCREEN_GADGETS              3
52
53 /* forward declaration for internal use */
54 static void HandleScreenGadgets(struct GadgetInfo *);
55
56 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
57
58 void DrawHeadline()
59 {
60   int x = SX + (SXSIZE - strlen(PROGRAM_TITLE_STRING) * FONT1_XSIZE) / 2;
61
62   DrawText(x, SY + 8, PROGRAM_TITLE_STRING, FS_BIG, FC_YELLOW);
63   DrawTextFCentered(46, FC_RED, COPYRIGHT_STRING);
64 }
65
66 void DrawMainMenu()
67 {
68   static struct LevelDirInfo *leveldir_last_valid = NULL;
69   int i;
70   char *name_text = (!options.network && setup.team_mode ? "Team:" : "Name:");
71
72   UnmapAllGadgets();
73   FadeSounds();
74   KeyboardAutoRepeatOn();
75
76   /* needed if last screen was the playing screen, invoked from level editor */
77   if (level_editor_test_game)
78   {
79     game_status = LEVELED;
80     DrawLevelEd();
81     return;
82   }
83
84   /* needed if last screen was the editor screen */
85   UndrawSpecialEditorDoor();
86
87   /* map gadgets for main menu screen */
88   MapTapeButtons();
89
90   /* leveldir_current may be invalid (level group, parent link) */
91   if (!validLevelSeries(leveldir_current))
92     leveldir_current = getFirstValidLevelSeries(leveldir_last_valid);
93
94   /* store valid level series information */
95   leveldir_last_valid = leveldir_current;
96
97   /* level_nr may have been set to value over handicap with level editor */
98   if (setup.handicap && level_nr > leveldir_current->handicap_level)
99     level_nr = leveldir_current->handicap_level;
100
101   GetPlayerConfig();
102   LoadLevel(level_nr);
103
104   ClearWindow();
105   DrawHeadline();
106   DrawText(SX + 32,    SY + 2*32, name_text, FS_BIG, FC_GREEN);
107   DrawText(SX + 6*32,  SY + 2*32, setup.player_name, FS_BIG, FC_RED);
108   DrawText(SX + 32,    SY + 3*32, "Level:", FS_BIG, FC_GREEN);
109   DrawText(SX + 11*32, SY + 3*32, int2str(level_nr,3), FS_BIG,
110            (leveldir_current->readonly ? FC_RED : FC_YELLOW));
111   DrawText(SX + 32,    SY + 4*32, "Hall Of Fame", FS_BIG, FC_GREEN);
112   DrawText(SX + 32,    SY + 5*32, "Level Creator", FS_BIG, FC_GREEN);
113   DrawText(SY + 32,    SY + 6*32, "Info Screen", FS_BIG, FC_GREEN);
114   DrawText(SX + 32,    SY + 7*32, "Start Game", FS_BIG, FC_GREEN);
115   DrawText(SX + 32,    SY + 8*32, "Setup", FS_BIG, FC_GREEN);
116   DrawText(SX + 32,    SY + 9*32, "Quit", FS_BIG, FC_GREEN);
117
118   DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, TRUE);
119
120   DrawTextF(7*32 + 6, 3*32 + 9, FC_RED, "%d-%d",
121             leveldir_current->first_level,
122             leveldir_current->last_level);
123
124   if (leveldir_current->readonly)
125   {
126     DrawTextF(15*32 + 6, 3*32 + 9 - 7, FC_RED, "READ");
127     DrawTextF(15*32 + 6, 3*32 + 9 + 7, FC_RED, "ONLY");
128   }
129
130   for(i=2; i<10; i++)
131     DrawGraphic(0, i, GFX_KUGEL_BLAU);
132   DrawGraphic(10, 3, GFX_ARROW_BLUE_LEFT);
133   DrawGraphic(14, 3, GFX_ARROW_BLUE_RIGHT);
134
135   DrawText(SX + 56, SY + 326, "A Game by Artsoft Entertainment",
136            FS_SMALL, FC_RED);
137
138   if (leveldir_current->name)
139   {
140     int len = strlen(leveldir_current->name);
141     int lxpos = SX + (SXSIZE - len * FONT4_XSIZE) / 2;
142     int lypos = SY + 352;
143
144     DrawText(lxpos, lypos, leveldir_current->name, FS_SMALL, FC_SPECIAL2);
145   }
146
147   FadeToFront();
148   InitAnimation();
149   HandleMainMenu(0,0, 0,0, MB_MENU_INITIALIZE);
150
151   TapeStop();
152   if (TAPE_IS_EMPTY(tape))
153     LoadTape(level_nr);
154   DrawCompleteVideoDisplay();
155
156   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
157
158 #if 0
159   ClearEventQueue();
160 #endif
161
162 }
163
164 static void gotoTopLevelDir()
165 {
166   /* move upwards to top level directory */
167   while (leveldir_current->node_parent)
168   {
169     /* write a "path" into level tree for easy navigation to last level */
170     if (leveldir_current->node_parent->node_group->cl_first == -1)
171     {
172       int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
173       int leveldir_pos = posLevelDirInfo(leveldir_current);
174       int num_page_entries;
175       int cl_first, cl_cursor;
176
177       if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
178         num_page_entries = num_leveldirs;
179       else
180         num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
181
182       cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
183       cl_cursor = leveldir_pos - cl_first + 3;
184
185       leveldir_current->node_parent->node_group->cl_first = cl_first;
186       leveldir_current->node_parent->node_group->cl_cursor = cl_cursor;
187     }
188
189     leveldir_current = leveldir_current->node_parent;
190   }
191 }
192
193 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
194 {
195   static int choice = 3;
196   static int redraw = TRUE;
197   int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
198
199   if (redraw || button == MB_MENU_INITIALIZE)
200   {
201     DrawGraphic(0, choice - 1, GFX_KUGEL_ROT);
202     redraw = FALSE;
203   }
204
205   if (button == MB_MENU_INITIALIZE)
206     return;
207
208   if (dx || dy)
209   {
210     if (dx && choice == 4)
211     {
212       x = (dx < 0 ? 11 : 15);
213       y = 4;
214     }
215     else if (dy)
216     {
217       x = 1;
218       y = choice + dy;
219     }
220     else
221       x = y = 0;
222
223     if (y < 3)
224       y = 3;
225     else if (y > 10)
226       y = 10;
227   }
228
229   if (!mx && !my && !dx && !dy)
230   {
231     x = 1;
232     y = choice;
233   }
234
235   if (y == 4 && ((x == 11 && level_nr > leveldir_current->first_level) ||
236                  (x == 15 && level_nr < leveldir_current->last_level)) &&
237       button)
238   {
239     static unsigned long level_delay = 0;
240     int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
241     int new_level_nr, old_level_nr = level_nr;
242     int font_color = (leveldir_current->readonly ? FC_RED : FC_YELLOW);
243
244     new_level_nr = level_nr + (x == 11 ? -step : +step);
245     if (new_level_nr < leveldir_current->first_level)
246       new_level_nr = leveldir_current->first_level;
247     if (new_level_nr > leveldir_current->last_level)
248       new_level_nr = leveldir_current->last_level;
249
250     if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
251       new_level_nr = leveldir_current->handicap_level;
252
253     if (old_level_nr == new_level_nr ||
254         !DelayReached(&level_delay, GADGET_FRAME_DELAY))
255       goto out;
256
257     level_nr = new_level_nr;
258
259     DrawTextExt(drawto, gc, SX + 11 * 32, SY + 3 * 32,
260                 int2str(level_nr, 3), FS_BIG, font_color);
261     DrawTextExt(window, gc, SX + 11 * 32, SY + 3 * 32,
262                 int2str(level_nr, 3), FS_BIG, font_color);
263
264     LoadLevel(level_nr);
265     DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, TRUE);
266
267     TapeErase();
268     LoadTape(level_nr);
269     DrawCompleteVideoDisplay();
270
271     /* needed because DrawMicroLevel() takes some time */
272     BackToFront();
273     SyncDisplay();
274     DelayReached(&level_delay, 0);      /* reset delay counter */
275   }
276   else if (x == 1 && y >= 3 && y <= 10)
277   {
278     if (button)
279     {
280       if (y != choice)
281       {
282         DrawGraphic(0, y - 1, GFX_KUGEL_ROT);
283         DrawGraphic(0, choice - 1, GFX_KUGEL_BLAU);
284         choice = y;
285       }
286     }
287     else
288     {
289       if (y == 3)
290       {
291         game_status = TYPENAME;
292         HandleTypeName(strlen(setup.player_name), 0);
293       }
294       else if (y == 4)
295       {
296         if (leveldir_first)
297         {
298           game_status = CHOOSELEVEL;
299           SaveLevelSetup_LastSeries();
300           SaveLevelSetup_SeriesInfo();
301
302           gotoTopLevelDir();
303
304           DrawChooseLevel();
305         }
306       }
307       else if (y == 5)
308       {
309         game_status = HALLOFFAME;
310         DrawHallOfFame(-1);
311       }
312       else if (y == 6)
313       {
314         if (leveldir_current->readonly &&
315             strcmp(setup.player_name, "Artsoft") != 0)
316           Request("This level is read only !", REQ_CONFIRM);
317         game_status = LEVELED;
318         DrawLevelEd();
319       }
320       else if (y == 7)
321       {
322         game_status = HELPSCREEN;
323         DrawHelpScreen();
324       }
325       else if (y == 8)
326       {
327         if (setup.autorecord)
328           TapeStartRecording();
329
330 #if !defined(MSDOS) && !defined(WIN32)
331         if (options.network)
332           SendToServer_StartPlaying();
333         else
334 #endif
335         {
336           game_status = PLAYING;
337           StopAnimation();
338           InitGame();
339         }
340       }
341       else if (y == 9)
342       {
343         game_status = SETUP;
344         DrawSetupScreen();
345       }
346       else if (y == 10)
347       {
348         SaveLevelSetup_LastSeries();
349         SaveLevelSetup_SeriesInfo();
350         if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
351           game_status = EXITGAME;
352       }
353
354       redraw = TRUE;
355     }
356   }
357   BackToFront();
358
359   out:
360
361   if (game_status == MAINMENU)
362   {
363     DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, FALSE);
364     DoAnimation();
365   }
366 }
367
368 #define MAX_HELPSCREEN_ELS      10
369 #define HA_NEXT                 -999
370 #define HA_END                  -1000
371
372 static long helpscreen_state;
373 static int helpscreen_step[MAX_HELPSCREEN_ELS];
374 static int helpscreen_frame[MAX_HELPSCREEN_ELS];
375 static int helpscreen_delay[MAX_HELPSCREEN_ELS];
376 static int helpscreen_action[] =
377 {
378   GFX_SPIELER1_DOWN,4,2,
379   GFX_SPIELER1_UP,4,2,
380   GFX_SPIELER1_LEFT,4,2,
381   GFX_SPIELER1_RIGHT,4,2,
382   GFX_SPIELER1_PUSH_LEFT,4,2,
383   GFX_SPIELER1_PUSH_RIGHT,4,2,                                  HA_NEXT,
384   GFX_ERDREICH,1,100,                                           HA_NEXT,
385   GFX_LEERRAUM,1,100,                                           HA_NEXT,
386   GFX_MORAST_LEER,1,100,                                        HA_NEXT,
387   GFX_BETON,1,100,                                              HA_NEXT,
388   GFX_MAUERWERK,1,100,                                          HA_NEXT,
389   GFX_MAUER_L1,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
390   GFX_MAUER_R1,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
391   GFX_MAUER_UP,  3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,
392   GFX_MAUER_DOWN,3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10,    HA_NEXT,
393   GFX_UNSICHTBAR,1,100,                                         HA_NEXT,
394   GFX_FELSBODEN,1,100,                                          HA_NEXT,
395   GFX_CHAR_A,30,4, GFX_CHAR_AUSRUF,32,4,                        HA_NEXT,
396   GFX_EDELSTEIN,2,5,                                            HA_NEXT,
397   GFX_DIAMANT,2,5,                                              HA_NEXT,
398   GFX_EDELSTEIN_BD,2,5,                                         HA_NEXT,
399   GFX_EDELSTEIN_GELB,2,5, GFX_EDELSTEIN_ROT,2,5,
400   GFX_EDELSTEIN_LILA,2,5,                                       HA_NEXT,
401   GFX_FELSBROCKEN,4,5,                                          HA_NEXT,
402   GFX_BOMBE,1,50, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10,         HA_NEXT,
403   GFX_KOKOSNUSS,1,50, GFX_CRACKINGNUT,3,1, GFX_EDELSTEIN,1,10,  HA_NEXT,
404   GFX_ERZ_EDEL,1,50, GFX_EXPLOSION,8,1, GFX_EDELSTEIN,1,10,     HA_NEXT,
405   GFX_ERZ_DIAM,1,50, GFX_EXPLOSION,8,1, GFX_DIAMANT,1,10,       HA_NEXT,
406   GFX_ERZ_EDEL_BD,1,50, GFX_EXPLOSION,8,1,GFX_EDELSTEIN_BD,1,10,HA_NEXT,
407   GFX_ERZ_EDEL_GELB,1,50, GFX_EXPLOSION,8,1,
408   GFX_EDELSTEIN_GELB,1,10, GFX_ERZ_EDEL_ROT,1,50,
409   GFX_EXPLOSION,8,1, GFX_EDELSTEIN_ROT,1,10,
410   GFX_ERZ_EDEL_LILA,1,50, GFX_EXPLOSION,8,1,
411   GFX_EDELSTEIN_LILA,1,10,                                      HA_NEXT,
412   GFX_GEBLUBBER,4,4,                                            HA_NEXT,
413   GFX_SCHLUESSEL1,4,25,                                         HA_NEXT,
414   GFX_PFORTE1,4,25,                                             HA_NEXT,
415   GFX_PFORTE1X,4,25,                                            HA_NEXT,
416   GFX_DYNAMIT_AUS,1,100,                                        HA_NEXT,
417   GFX_DYNAMIT,7,6, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10,        HA_NEXT,
418   GFX_DYNABOMB+0,4,3, GFX_DYNABOMB+3,1,3, GFX_DYNABOMB+2,1,3,
419   GFX_DYNABOMB+1,1,3, GFX_DYNABOMB+0,1,3, GFX_EXPLOSION,8,1,
420   GFX_LEERRAUM,1,10,                                            HA_NEXT,
421   GFX_DYNABOMB_NR,1,100,                                        HA_NEXT,
422   GFX_DYNABOMB_SZ,1,100,                                        HA_NEXT,
423   GFX_FLIEGER+4,1,3, GFX_FLIEGER+0,1,3, GFX_FLIEGER+4,1,3,
424   GFX_FLIEGER+5,1,3, GFX_FLIEGER+1,1,3, GFX_FLIEGER+5,1,3,
425   GFX_FLIEGER+6,1,3, GFX_FLIEGER+2,1,3, GFX_FLIEGER+6,1,3,
426   GFX_FLIEGER+7,1,3, GFX_FLIEGER+3,1,3, GFX_FLIEGER+7,1,3,      HA_NEXT,
427   GFX_KAEFER+4,1,1, GFX_KAEFER+0,1,1, GFX_KAEFER+4,1,1,
428   GFX_KAEFER+5,1,1, GFX_KAEFER+1,1,1, GFX_KAEFER+5,1,1,
429   GFX_KAEFER+6,1,1, GFX_KAEFER+2,1,1, GFX_KAEFER+6,1,1,
430   GFX_KAEFER+7,1,1, GFX_KAEFER+3,1,1, GFX_KAEFER+7,1,1,         HA_NEXT,
431   GFX_BUTTERFLY,2,2,                                            HA_NEXT,
432   GFX_FIREFLY,2,2,                                              HA_NEXT,
433   GFX_PACMAN+0,1,3, GFX_PACMAN+4,1,2, GFX_PACMAN+0,1,3,
434   GFX_PACMAN+1,1,3, GFX_PACMAN+5,1,2, GFX_PACMAN+1,1,3,
435   GFX_PACMAN+2,1,3, GFX_PACMAN+6,1,2, GFX_PACMAN+2,1,3,
436   GFX_PACMAN+3,1,3, GFX_PACMAN+7,1,2, GFX_PACMAN+3,1,3,         HA_NEXT,
437   GFX_MAMPFER+0,4,1, GFX_MAMPFER+3,1,1, GFX_MAMPFER+2,1,1,
438   GFX_MAMPFER+1,1,1, GFX_MAMPFER+0,1,1,                         HA_NEXT,
439   GFX_MAMPFER2+0,4,1, GFX_MAMPFER2+3,1,1, GFX_MAMPFER2+2,1,1,
440   GFX_MAMPFER2+1,1,1, GFX_MAMPFER2+0,1,1,                       HA_NEXT,
441   GFX_ROBOT+0,4,1, GFX_ROBOT+3,1,1, GFX_ROBOT+2,1,1,
442   GFX_ROBOT+1,1,1, GFX_ROBOT+0,1,1,                             HA_NEXT,
443   GFX_MOLE_DOWN,4,2,
444   GFX_MOLE_UP,4,2,
445   GFX_MOLE_LEFT,4,2,
446   GFX_MOLE_RIGHT,4,2,                                           HA_NEXT,
447   GFX_PINGUIN_DOWN,4,2,
448   GFX_PINGUIN_UP,4,2,
449   GFX_PINGUIN_LEFT,4,2,
450   GFX_PINGUIN_RIGHT,4,2,                                        HA_NEXT,
451   GFX_SCHWEIN_DOWN,4,2,
452   GFX_SCHWEIN_UP,4,2,
453   GFX_SCHWEIN_LEFT,4,2,
454   GFX_SCHWEIN_RIGHT,4,2,                                        HA_NEXT,
455   GFX_DRACHE_DOWN,4,2,
456   GFX_DRACHE_UP,4,2,
457   GFX_DRACHE_LEFT,4,2,
458   GFX_DRACHE_RIGHT,4,2,                                         HA_NEXT,
459   GFX_SONDE_START,8,1,                                          HA_NEXT,
460   GFX_ABLENK,4,1,                                               HA_NEXT,
461   GFX_BIRNE_AUS,1,25, GFX_BIRNE_EIN,1,25,                       HA_NEXT,
462   GFX_ZEIT_VOLL,1,25, GFX_ZEIT_LEER,1,25,                       HA_NEXT,
463   GFX_TROPFEN,1,25, GFX_AMOEBING,4,1, GFX_AMOEBE_LEBT,1,10,     HA_NEXT,
464   GFX_AMOEBE_TOT+2,2,50, GFX_AMOEBE_TOT,2,50,                   HA_NEXT,
465   GFX_AMOEBE_LEBT,4,40,                                         HA_NEXT,
466   GFX_AMOEBE_LEBT,1,10, GFX_AMOEBING,4,2,                       HA_NEXT,
467   GFX_AMOEBE_LEBT,1,25, GFX_AMOEBE_TOT,1,25, GFX_EXPLOSION,8,1,
468   GFX_DIAMANT,1,10,                                             HA_NEXT,
469   GFX_LIFE,1,100,                                               HA_NEXT,
470   GFX_LIFE_ASYNC,1,100,                                         HA_NEXT,
471   GFX_MAGIC_WALL_OFF,4,2,                                       HA_NEXT,
472   GFX_MAGIC_WALL_BD_OFF,4,2,                                    HA_NEXT,
473   GFX_AUSGANG_ZU,1,100, GFX_AUSGANG_ACT,4,2,
474   GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2,
475   GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2,                 HA_NEXT,
476   GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2,
477   GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2,                 HA_NEXT,
478   GFX_SOKOBAN_OBJEKT,1,100,                                     HA_NEXT,
479   GFX_SOKOBAN_FELD_LEER,1,100,                                  HA_NEXT,
480   GFX_SOKOBAN_FELD_VOLL,1,100,                                  HA_NEXT,
481   GFX_SPEED_PILL,1,100,                                         HA_NEXT,
482   HA_END
483 };
484 static char *helpscreen_eltext[][2] =
485 {
486  {"THE HERO:",                          "(Is _this_ guy good old Rockford?)"},
487  {"Normal sand:",                       "You can dig through it"},
488  {"Empty field:",                       "You can walk through it"},
489  {"Quicksand: You cannot pass it,",     "but rocks can fall though it"},
490  {"Massive Wall:",                      "Nothing can go through it"},
491  {"Normal Wall: You can't go through",  "it, but you can bomb it away"},
492  {"Growing Wall: Grows in several di-", "rections if there is an empty field"},
493  {"Invisible Wall: Behaves like normal","wall, but is invisible"},
494  {"Old Wall: Like normal wall, but",    "some things can fall down from it"},
495  {"Letter Wall: Looks like a letter,",  "behaves like a normal wall"},
496  {"Emerald: You must collect enough of","them to finish a level"},
497  {"Diamond: Counts as 3 emeralds, but", "can be destroyed by rocks"},
498  {"Diamond (BD style): Counts like one","emerald and behaves a bit different"},
499  {"Colorful Gems:",                     "Seem to behave like Emeralds"},
500  {"Rock: Smashes several things;",      "Can be moved by the player"},
501  {"Bomb: You can move it, but be",      "careful when dropping it"},
502  {"Nut: Throw a rock on it to open it;","Each nut contains an emerald"},
503  {"Wall with an emerald inside:",       "Bomb the wall away to get it"},
504  {"Wall with a diamond inside:",        "Bomb the wall away to get it"},
505  {"Wall with BD style diamond inside:", "Bomb the wall away to get it"},
506  {"Wall with colorful gem inside:",     "Bomb the wall away to get it"},
507  {"Acid: Things that fall in are gone", "forever (including our hero)"},
508  {"Key: Opens the door that has the",   "same color (red/yellow/green/blue)"},
509  {"Door: Can be opened by the key",     "with the same color"},
510  {"Door: You have to find out the",     "right color of the key for it"},
511  {"Dynamite: Collect it and use it to", "destroy walls or kill enemies"},
512  {"Dynamite: This one explodes after",  "a few seconds"},
513  {"Dyna Bomb: Explodes in 4 directions","with variable explosion size"},
514  {"Dyna Bomb: Increases the number of", "dyna bombs available at a time"},
515  {"Dyna Bomb: Increases the size of",   "explosion of dyna bombs"},
516  {"Spaceship: Moves at the left side",  "of walls; don't touch it!"},
517  {"Bug: Moves at the right side",       "of walls; don't touch it!"},
518  {"Butterfly: Moves at the right side", "of walls; don't touch it!"},
519  {"Firefly: Moves at the left side",    "of walls; don't touch it!"},
520  {"Pacman: Eats the amoeba and you,",   "if you're not careful"},
521  {"Cruncher: Eats diamonds and you,",   "if you're not careful"},
522  {"Cruncher (BD style):",               "Eats almost everything"},
523  {"Robot: Tries to kill the player",    ""},
524  {"The mole: Eats the amoeba and turns","empty space into normal sand"},
525  {"The penguin: Guide him to the exit,","but keep him away from monsters!"},
526  {"The Pig: Harmless, but eats all",    "gems it can get"},
527  {"The Dragon: Breathes fire,",         "especially to some monsters"},
528  {"Sonde: Follows you everywhere;",     "harmless, but may block your way"},
529  {"Magic Wheel: Touch it to get rid of","the robots for some seconds"},
530  {"Light Bulb: All of them must be",    "switched on to finish a level"},
531  {"Extra Time Orb: Adds some seconds",  "to the time available for the level"},
532  {"Amoeba Drop: Grows to an amoeba on", "the ground - don't touch it"},
533  {"Dead Amoeba: Does not grow, but",    "can still kill bugs and spaceships"},
534  {"Normal Amoeba: Grows through empty", "fields, sand and quicksand"},
535  {"Dropping Amoeba: This one makes",    "drops that grow to a new amoeba"},
536  {"Living Amoeba (BD style): Contains", "other element, when surrounded"},
537  {"Game Of Life: Behaves like the well","known 'Game Of Life' (2333 style)"},
538  {"Biomaze: A bit like the 'Game Of",   "Life', but builds crazy mazes"},
539  {"Magic Wall: Changes rocks, emeralds","and diamonds when they pass it"},
540  {"Magic Wall (BD style):",             "Changes rocks and BD style diamonds"},
541  {"Exit door: Opens if you have enough","emeralds to finish the level"},
542  {"Open exit door: Enter here to leave","the level and exit the actual game"},
543  {"Sokoban element: Object which must", "be pushed to an empty field"},
544  {"Sokoban element: Empty field where", "a Sokoban object can be placed on"},
545  {"Sokoban element: Field with object", "which can be pushed away"},
546  {"Speed pill: Lets the player run",    "twice as fast as normally"},
547 };
548 static int num_helpscreen_els = sizeof(helpscreen_eltext)/(2*sizeof(char *));
549
550 static char *helpscreen_music[][3] =
551 {
552   { "Alchemy",                  "Ian Boddy",            "Drive" },
553   { "The Chase",                "Propaganda",           "A Secret Wish" },
554   { "Network 23",               "Tangerine Dream",      "Exit" },
555   { "Czardasz",                 "Robert Pieculewicz",   "Czardasz" },
556   { "21st Century Common Man",  "Tangerine Dream",      "Tyger" },
557   { "Voyager",                  "The Alan Parsons Project","Pyramid" },
558   { "Twilight Painter",         "Tangerine Dream",      "Heartbreakers" }
559 };
560 static int helpscreen_musicpos;
561
562 void DrawHelpScreenElAction(int start)
563 {
564   int i = 0, j = 0;
565   int frame, graphic;
566   int xstart = SX+16, ystart = SY+64+2*32, ystep = TILEY+4;
567
568   while(helpscreen_action[j] != HA_END)
569   {
570     if (i>=start+MAX_HELPSCREEN_ELS || i>=num_helpscreen_els)
571       break;
572     else if (i<start || helpscreen_delay[i-start])
573     {
574       if (i>=start && helpscreen_delay[i-start])
575         helpscreen_delay[i-start]--;
576
577       while(helpscreen_action[j] != HA_NEXT)
578         j++;
579       j++;
580       i++;
581       continue;
582     }
583
584     j += 3*helpscreen_step[i-start];
585     graphic = helpscreen_action[j++];
586
587     if (helpscreen_frame[i-start])
588     {
589       frame = helpscreen_action[j++] - helpscreen_frame[i-start];
590       helpscreen_frame[i-start]--;
591     }
592     else
593     {
594       frame = 0;
595       helpscreen_frame[i-start] = helpscreen_action[j++]-1;
596     }
597
598     helpscreen_delay[i-start] = helpscreen_action[j++] - 1;
599
600     if (helpscreen_action[j] == HA_NEXT)
601     {
602       if (!helpscreen_frame[i-start])
603         helpscreen_step[i-start] = 0;
604     }
605     else
606     {
607       if (!helpscreen_frame[i-start])
608         helpscreen_step[i-start]++;
609       while(helpscreen_action[j] != HA_NEXT)
610         j++;
611     }
612     j++;
613
614     DrawGraphicExt(drawto, gc, xstart, ystart+(i-start)*ystep, graphic+frame);
615     i++;
616   }
617
618   for(i=2;i<16;i++)
619   {
620     MarkTileDirty(0,i);
621     MarkTileDirty(1,i);
622   }
623 }
624
625 void DrawHelpScreenElText(int start)
626 {
627   int i;
628   int xstart = SX + 56, ystart = SY + 65 + 2 * 32, ystep = TILEY + 4;
629   int ybottom = SYSIZE - 20;
630
631   ClearWindow();
632   DrawHeadline();
633
634   DrawTextFCentered(100, FC_GREEN, "The game elements:");
635
636   for(i=start; i < start + MAX_HELPSCREEN_ELS && i < num_helpscreen_els; i++)
637   {
638     DrawText(xstart,
639              ystart + (i - start) * ystep + (*helpscreen_eltext[i][1] ? 0 : 8),
640              helpscreen_eltext[i][0], FS_SMALL, FC_YELLOW);
641     DrawText(xstart, ystart + (i - start) * ystep + 16,
642              helpscreen_eltext[i][1], FS_SMALL, FC_YELLOW);
643   }
644
645   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
646 }
647
648 void DrawHelpScreenMusicText(int num)
649 {
650   int ystart = 150, ystep = 30;
651   int ybottom = SYSIZE - 20;
652
653   FadeSounds();
654   ClearWindow();
655   DrawHeadline();
656
657   DrawTextFCentered(100, FC_GREEN, "The game background music loops:");
658
659   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
660                     "Excerpt from");
661   DrawTextFCentered(ystart + 1 * ystep, FC_RED, "\"%s\"",
662                     helpscreen_music[num][0]);
663   DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW,
664                     "by");
665   DrawTextFCentered(ystart + 3 * ystep, FC_RED,
666                     "%s", helpscreen_music[num][1]);
667   DrawTextFCentered(ystart + 4 * ystep, FC_YELLOW,
668                     "from the album");
669   DrawTextFCentered(ystart + 5 * ystep, FC_RED, "\"%s\"",
670                     helpscreen_music[num][2]);
671
672   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
673
674   PlaySoundLoop(background_loop[num]);
675 }
676
677 void DrawHelpScreenCreditsText()
678 {
679   int ystart = 150, ystep = 30;
680   int ybottom = SYSIZE - 20;
681
682   FadeSounds();
683   ClearWindow();
684   DrawHeadline();
685
686   DrawTextFCentered(100, FC_GREEN,
687                     "Credits:");
688   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
689                     "DOS port of the game:");
690   DrawTextFCentered(ystart + 1 * ystep, FC_RED,
691                     "Guido Schulz");
692   DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW,
693                     "Additional toons:");
694   DrawTextFCentered(ystart + 3 * ystep, FC_RED,
695                     "Karl Hörnell");
696   DrawTextFCentered(ystart + 5 * ystep, FC_YELLOW,
697                     "...and many thanks to all contributors");
698   DrawTextFCentered(ystart + 6 * ystep, FC_YELLOW,
699                     "of new levels!");
700
701   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page");
702 }
703
704 void DrawHelpScreenContactText()
705 {
706   int ystart = 150, ystep = 30;
707   int ybottom = SYSIZE - 20;
708
709   ClearWindow();
710   DrawHeadline();
711
712   DrawTextFCentered(100, FC_GREEN, "Program information:");
713
714   DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW,
715                     "This game is Freeware!");
716   DrawTextFCentered(ystart + 1 * ystep, FC_YELLOW,
717                     "If you like it, send e-mail to:");
718   DrawTextFCentered(ystart + 2 * ystep, FC_RED,
719                     "info@artsoft.org");
720   DrawTextFCentered(ystart + 3 * ystep, FC_YELLOW,
721                     "or SnailMail to:");
722   DrawTextFCentered(ystart + 4 * ystep + 0, FC_RED,
723                     "Holger Schemel");
724   DrawTextFCentered(ystart + 4 * ystep + 20, FC_RED,
725                     "Detmolder Strasse 189");
726   DrawTextFCentered(ystart + 4 * ystep + 40, FC_RED,
727                     "33604 Bielefeld");
728   DrawTextFCentered(ystart + 4 * ystep + 60, FC_RED,
729                     "Germany");
730
731   DrawTextFCentered(ystart + 7 * ystep, FC_YELLOW,
732                     "If you have created new levels,");
733   DrawTextFCentered(ystart + 8 * ystep, FC_YELLOW,
734                     "send them to me to include them!");
735   DrawTextFCentered(ystart + 9 * ystep, FC_YELLOW,
736                     ":-)");
737
738   DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for main menu");
739 }
740
741 void DrawHelpScreen()
742 {
743   int i;
744
745   UnmapAllGadgets();
746   CloseDoor(DOOR_CLOSE_2);
747
748   for(i=0;i<MAX_HELPSCREEN_ELS;i++)
749     helpscreen_step[i] = helpscreen_frame[i] = helpscreen_delay[i] = 0;
750   helpscreen_musicpos = 0;
751   helpscreen_state = 0;
752   DrawHelpScreenElText(0);
753   DrawHelpScreenElAction(0);
754
755   FadeToFront();
756   InitAnimation();
757   PlaySoundLoop(SND_RHYTHMLOOP);
758 }
759
760 void HandleHelpScreen(int button)
761 {
762   static unsigned long hs_delay = 0;
763   int num_helpscreen_els_pages =
764     (num_helpscreen_els + MAX_HELPSCREEN_ELS-1) / MAX_HELPSCREEN_ELS;
765   int button_released = !button;
766   int i;
767
768   if (button_released)
769   {
770     if (helpscreen_state < num_helpscreen_els_pages - 1)
771     {
772       for(i=0;i<MAX_HELPSCREEN_ELS;i++)
773         helpscreen_step[i] = helpscreen_frame[i] = helpscreen_delay[i] = 0;
774       helpscreen_state++;
775       DrawHelpScreenElText(helpscreen_state*MAX_HELPSCREEN_ELS);
776       DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
777     }
778     else if (helpscreen_state < num_helpscreen_els_pages + num_bg_loops - 1)
779     {
780       helpscreen_state++;
781       DrawHelpScreenMusicText(helpscreen_state - num_helpscreen_els_pages);
782     }
783     else if (helpscreen_state == num_helpscreen_els_pages + num_bg_loops - 1)
784     {
785       helpscreen_state++;
786       DrawHelpScreenCreditsText();
787     }
788     else if (helpscreen_state == num_helpscreen_els_pages + num_bg_loops)
789     {
790       helpscreen_state++;
791       DrawHelpScreenContactText();
792     }
793     else
794     {
795       FadeSounds();
796       DrawMainMenu();
797       game_status = MAINMENU;
798     }
799   }
800   else
801   {
802     if (DelayReached(&hs_delay,GAME_FRAME_DELAY * 2))
803     {
804       if (helpscreen_state<num_helpscreen_els_pages)
805         DrawHelpScreenElAction(helpscreen_state*MAX_HELPSCREEN_ELS);
806     }
807     DoAnimation();
808   }
809
810   BackToFront();
811 }
812
813 void HandleTypeName(int newxpos, Key key)
814 {
815   static int xpos = 0, ypos = 2;
816
817   if (newxpos)
818   {
819     xpos = newxpos;
820     DrawText(SX + 6*32, SY + ypos*32, setup.player_name, FS_BIG, FC_YELLOW);
821     DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
822     return;
823   }
824
825   if (((key >= KEY_A && key <= KEY_Z) || (key >= KEY_a && key <= KEY_z)) && 
826       xpos < MAX_PLAYER_NAME_LEN)
827   {
828     char ascii;
829
830     if (key >= KEY_A && key <= KEY_Z)
831       ascii = 'A' + (char)(key - KEY_A);
832     else
833       ascii = 'a' + (char)(key - KEY_a);
834
835     setup.player_name[xpos] = ascii;
836     setup.player_name[xpos + 1] = 0;
837     xpos++;
838     DrawTextExt(drawto, gc, SX + 6*32, SY + ypos*32,
839                 setup.player_name, FS_BIG, FC_YELLOW);
840     DrawTextExt(window, gc, SX + 6*32, SY + ypos*32,
841                 setup.player_name, FS_BIG, FC_YELLOW);
842     DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
843   }
844   else if ((key == KEY_Delete || key == KEY_BackSpace) && xpos > 0)
845   {
846     xpos--;
847     setup.player_name[xpos] = 0;
848     DrawGraphic(xpos + 6, ypos, GFX_KUGEL_ROT);
849     DrawGraphic(xpos + 7, ypos, GFX_LEERRAUM);
850   }
851   else if (key == KEY_Return && xpos > 0)
852   {
853     DrawText(SX + 6*32, SY + ypos*32, setup.player_name, FS_BIG, FC_RED);
854     DrawGraphic(xpos + 6, ypos, GFX_LEERRAUM);
855
856     SaveSetup();
857     game_status = MAINMENU;
858   }
859
860   BackToFront();
861 }
862
863 static void drawCursorExt(int ypos, int color, int graphic)
864 {
865   static int cursor_array[SCR_FIELDY];
866
867   if (graphic)
868     cursor_array[ypos] = graphic;
869
870   graphic = cursor_array[ypos];
871
872   if (color == FC_RED)
873     graphic = (graphic == GFX_ARROW_BLUE_LEFT  ? GFX_ARROW_RED_LEFT  :
874                graphic == GFX_ARROW_BLUE_RIGHT ? GFX_ARROW_RED_RIGHT :
875                GFX_KUGEL_ROT);
876
877   DrawGraphic(0, ypos, graphic);
878 }
879
880 static void initCursor(int ypos, int graphic)
881 {
882   drawCursorExt(ypos, FC_BLUE, graphic);
883 }
884
885 static void drawCursor(int ypos, int color)
886 {
887   drawCursorExt(ypos, color, 0);
888 }
889
890 void DrawChooseLevel()
891 {
892   UnmapAllGadgets();
893   CloseDoor(DOOR_CLOSE_2);
894
895   ClearWindow();
896   HandleChooseLevel(0,0, 0,0, MB_MENU_INITIALIZE);
897   MapChooseLevelGadgets();
898
899   FadeToFront();
900   InitAnimation();
901 }
902
903 static void AdjustChooseLevelScrollbar(int id, int first_entry)
904 {
905   struct GadgetInfo *gi = screen_gadget[id];
906   int items_max, items_visible, item_position;
907
908   items_max = numLevelDirInfoInGroup(leveldir_current);
909   items_visible = MAX_LEVEL_SERIES_ON_SCREEN - 1;
910   item_position = first_entry;
911
912   if (item_position > items_max - items_visible)
913     item_position = items_max - items_visible;
914
915   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
916                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
917 }
918
919 static void drawChooseLevelList(int first_entry, int num_page_entries)
920 {
921   int i;
922   char buffer[SCR_FIELDX * 2];
923   int max_buffer_len = (SCR_FIELDX - 2) * 2;
924   int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
925
926   ClearRectangle(backbuffer, SX, SY, SXSIZE - 32, SYSIZE);
927   redraw_mask |= REDRAW_FIELD;
928
929   DrawText(SX, SY, "Level Directories", FS_BIG, FC_GREEN);
930
931   for(i=0; i<num_page_entries; i++)
932   {
933     struct LevelDirInfo *node, *node_first;
934     int leveldir_pos = first_entry + i;
935     int ypos = i + 2;
936
937     node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
938     node = getLevelDirInfoFromPos(node_first, leveldir_pos);
939
940     strncpy(buffer, node->name , max_buffer_len);
941     buffer[max_buffer_len] = '\0';
942
943     DrawText(SX + 32, SY + ypos * 32, buffer, FS_MEDIUM, node->color);
944
945     if (node->parent_link)
946       initCursor(ypos, GFX_ARROW_BLUE_LEFT);
947     else if (node->level_group)
948       initCursor(ypos, GFX_ARROW_BLUE_RIGHT);
949     else
950       initCursor(ypos, GFX_KUGEL_BLAU);
951   }
952
953   if (first_entry > 0)
954     DrawGraphic(0, 1, GFX_ARROW_BLUE_UP);
955
956   if (first_entry + num_page_entries < num_leveldirs)
957     DrawGraphic(0, MAX_LEVEL_SERIES_ON_SCREEN + 1, GFX_ARROW_BLUE_DOWN);
958 }
959
960 static void drawChooseLevelInfo(int leveldir_pos)
961 {
962   struct LevelDirInfo *node, *node_first;
963   int x, last_redraw_mask = redraw_mask;
964
965   node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
966   node = getLevelDirInfoFromPos(node_first, leveldir_pos);
967
968   ClearRectangle(drawto, SX + 32, SY + 32, SXSIZE - 64, 32);
969
970   if (node->parent_link)
971     DrawTextFCentered(40, FC_RED, "leave group \"%s\"", node->class_desc);
972   else if (node->level_group)
973     DrawTextFCentered(40, FC_RED, "enter group \"%s\"", node->class_desc);
974   else
975     DrawTextFCentered(40, FC_RED, "%3d levels (%s)",
976                       node->levels, node->class_desc);
977
978   /* let BackToFront() redraw only what is needed */
979   redraw_mask = last_redraw_mask | REDRAW_TILES;
980   for (x=0; x<SCR_FIELDX; x++)
981     MarkTileDirty(x, 1);
982 }
983
984 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
985 {
986   static unsigned long choose_delay = 0;
987   static int redraw = TRUE;
988   int x = (mx + 32 - SX) / 32, y = (my + 32 - SY) / 32;
989   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
990   int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
991   int num_page_entries;
992
993   if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
994     num_page_entries = num_leveldirs;
995   else
996     num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
997
998   if (button == MB_MENU_INITIALIZE)
999   {
1000     int leveldir_pos = posLevelDirInfo(leveldir_current);
1001
1002     if (leveldir_current->cl_first == -1)
1003     {
1004       leveldir_current->cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
1005       leveldir_current->cl_cursor =
1006         leveldir_pos - leveldir_current->cl_first + 3;
1007     }
1008
1009     if (dx == 999)      /* first entry is set by scrollbar position */
1010       leveldir_current->cl_first = dy;
1011     else
1012       AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
1013                                  leveldir_current->cl_first);
1014
1015     drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
1016     drawChooseLevelInfo(leveldir_pos);
1017     redraw = TRUE;
1018   }
1019
1020   if (redraw)
1021   {
1022     drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
1023     redraw = FALSE;
1024   }
1025
1026   if (button == MB_MENU_INITIALIZE)
1027     return;
1028
1029   if (dx || dy)
1030   {
1031     if (dy)
1032     {
1033       x = 1;
1034       y = leveldir_current->cl_cursor + dy;
1035     }
1036     else
1037       x = y = 0;        /* no action */
1038
1039     if (ABS(dy) == SCR_FIELDY)  /* handle KEY_Page_Up, KEY_Page_Down */
1040     {
1041       dy = SIGN(dy);
1042       step = num_page_entries - 1;
1043       x = 1;
1044       y = (dy < 0 ? 2 : num_page_entries + 3);
1045     }
1046   }
1047
1048   if (x == 1 && y == 2)
1049   {
1050     if (leveldir_current->cl_first > 0 &&
1051         (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY)))
1052     {
1053       leveldir_current->cl_first -= step;
1054       if (leveldir_current->cl_first < 0)
1055         leveldir_current->cl_first = 0;
1056
1057       drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
1058       drawChooseLevelInfo(leveldir_current->cl_first +
1059                           leveldir_current->cl_cursor - 3);
1060       drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
1061       AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
1062                                  leveldir_current->cl_first);
1063       return;
1064     }
1065   }
1066   else if (x == 1 && y > num_page_entries + 2)
1067   {
1068     if (leveldir_current->cl_first + num_page_entries < num_leveldirs &&
1069         (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY)))
1070     {
1071       leveldir_current->cl_first += step;
1072       if (leveldir_current->cl_first + num_page_entries > num_leveldirs)
1073         leveldir_current->cl_first = MAX(0, num_leveldirs - num_page_entries);
1074
1075       drawChooseLevelList(leveldir_current->cl_first, num_page_entries);
1076       drawChooseLevelInfo(leveldir_current->cl_first +
1077                           leveldir_current->cl_cursor - 3);
1078       drawCursor(leveldir_current->cl_cursor - 1, FC_RED);
1079       AdjustChooseLevelScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
1080                                  leveldir_current->cl_first);
1081       return;
1082     }
1083   }
1084
1085   if (!mx && !my && !dx && !dy)
1086   {
1087     x = 1;
1088     y = leveldir_current->cl_cursor;
1089   }
1090
1091   if (dx == 1)
1092   {
1093     struct LevelDirInfo *node_first, *node_cursor;
1094     int leveldir_pos =
1095       leveldir_current->cl_first + leveldir_current->cl_cursor - 3;
1096
1097     node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
1098     node_cursor = getLevelDirInfoFromPos(node_first, leveldir_pos);
1099
1100     if (node_cursor->node_group)
1101     {
1102       node_cursor->cl_first = leveldir_current->cl_first;
1103       node_cursor->cl_cursor = leveldir_current->cl_cursor;
1104       leveldir_current = node_cursor->node_group;
1105       DrawChooseLevel();
1106     }
1107   }
1108   else if (dx == -1 && leveldir_current->node_parent)
1109   {
1110     leveldir_current = leveldir_current->node_parent;
1111     DrawChooseLevel();
1112   }
1113
1114   if (x == 1 && y >= 3 && y <= num_page_entries + 2)
1115   {
1116     if (button)
1117     {
1118       if (y != leveldir_current->cl_cursor)
1119       {
1120         drawCursor(y - 1, FC_RED);
1121         drawCursor(leveldir_current->cl_cursor - 1, FC_BLUE);
1122         drawChooseLevelInfo(leveldir_current->cl_first + y - 3);
1123         leveldir_current->cl_cursor = y;
1124       }
1125     }
1126     else
1127     {
1128       struct LevelDirInfo *node_first, *node_cursor;
1129       int leveldir_pos = leveldir_current->cl_first + y - 3;
1130
1131       node_first = getLevelDirInfoFirstGroupEntry(leveldir_current);
1132       node_cursor = getLevelDirInfoFromPos(node_first, leveldir_pos);
1133
1134       if (node_cursor->node_group)
1135       {
1136         node_cursor->cl_first = leveldir_current->cl_first;
1137         node_cursor->cl_cursor = leveldir_current->cl_cursor;
1138         leveldir_current = node_cursor->node_group;
1139         DrawChooseLevel();
1140       }
1141       else if (node_cursor->parent_link)
1142       {
1143         leveldir_current = node_cursor->node_parent;
1144         DrawChooseLevel();
1145       }
1146       else
1147       {
1148         leveldir_current = node_cursor;
1149
1150         LoadLevelSetup_SeriesInfo();
1151
1152         SaveLevelSetup_LastSeries();
1153         SaveLevelSetup_SeriesInfo();
1154         TapeErase();
1155
1156         game_status = MAINMENU;
1157         DrawMainMenu();
1158       }
1159     }
1160   }
1161
1162   BackToFront();
1163
1164   if (game_status == CHOOSELEVEL)
1165     DoAnimation();
1166 }
1167
1168 void DrawHallOfFame(int highlight_position)
1169 {
1170   UnmapAllGadgets();
1171   CloseDoor(DOOR_CLOSE_2);
1172
1173   if (highlight_position < 0) 
1174     LoadScore(level_nr);
1175
1176   FadeToFront();
1177   InitAnimation();
1178   HandleHallOfFame(highlight_position,0, 0,0, MB_MENU_INITIALIZE);
1179   PlaySound(SND_HALLOFFAME);
1180 }
1181
1182 static void drawHallOfFameList(int first_entry, int highlight_position)
1183 {
1184   int i;
1185
1186   ClearWindow();
1187   DrawText(SX + 80, SY + 8, "Hall Of Fame", FS_BIG, FC_YELLOW);
1188   DrawTextFCentered(46, FC_RED, "HighScores of Level %d", level_nr);
1189
1190   for(i=0; i<MAX_LEVEL_SERIES_ON_SCREEN; i++)
1191   {
1192     int entry = first_entry + i;
1193     int color = (entry == highlight_position ? FC_RED : FC_GREEN);
1194
1195 #if 0
1196     DrawText(SX, SY + 64 + i * 32, ".................", FS_BIG, color);
1197     DrawText(SX, SY + 64 + i * 32, highscore[i].Name, FS_BIG, color);
1198     DrawText(SX + 12 * 32, SY + 64 + i * 32,
1199              int2str(highscore[i].Score, 5), FS_BIG, color);
1200 #else
1201     DrawText(SX, SY + 64 + i * 32, "..................................",
1202              FS_MEDIUM, FC_YELLOW);
1203     DrawText(SX, SY + 64 + i * 32, int2str(entry + 1, 3),
1204              FS_MEDIUM, FC_YELLOW);
1205     DrawText(SX + 64, SY + 64 + i * 32, highscore[entry].Name, FS_BIG, color);
1206     DrawText(SX + 14 * 32 + 16, SY + 64 + i * 32,
1207              int2str(highscore[entry].Score, 5), FS_MEDIUM, color);
1208 #endif
1209   }
1210 }
1211
1212 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
1213 {
1214   static int first_entry = 0;
1215   static int highlight_position = 0;
1216   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
1217   int button_released = !button;
1218
1219   if (button == MB_MENU_INITIALIZE)
1220   {
1221     first_entry = 0;
1222     highlight_position = mx;
1223     drawHallOfFameList(first_entry, highlight_position);
1224     return;
1225   }
1226
1227   if (ABS(dy) == SCR_FIELDY)    /* handle KEY_Page_Up, KEY_Page_Down */
1228     step = MAX_LEVEL_SERIES_ON_SCREEN - 1;
1229
1230   if (dy < 0)
1231   {
1232     if (first_entry > 0)
1233     {
1234       first_entry -= step;
1235       if (first_entry < 0)
1236         first_entry = 0;
1237
1238       drawHallOfFameList(first_entry, highlight_position);
1239       return;
1240     }
1241   }
1242   else if (dy > 0)
1243   {
1244     if (first_entry + MAX_LEVEL_SERIES_ON_SCREEN < MAX_SCORE_ENTRIES)
1245     {
1246       first_entry += step;
1247       if (first_entry + MAX_LEVEL_SERIES_ON_SCREEN > MAX_SCORE_ENTRIES)
1248         first_entry = MAX(0, MAX_SCORE_ENTRIES - MAX_LEVEL_SERIES_ON_SCREEN);
1249
1250       drawHallOfFameList(first_entry, highlight_position);
1251       return;
1252     }
1253   }
1254
1255   if (button_released)
1256   {
1257     FadeSound(SND_HALLOFFAME);
1258     game_status = MAINMENU;
1259     DrawMainMenu();
1260   }
1261
1262   BackToFront();
1263
1264   if (game_status == HALLOFFAME)
1265     DoAnimation();
1266 }
1267
1268 void DrawSetupScreen()
1269 {
1270   int i;
1271   static struct setup
1272   {
1273     boolean *value;
1274     char *text;
1275   } setup_info[] =
1276   {
1277     { &setup.sound,             "Sound:",       },
1278     { &setup.sound_loops,       " Sound Loops:" },
1279     { &setup.sound_music,       " Game Music:"  },
1280 #if 0
1281     { &setup.toons,             "Toons:"        },
1282     { &setup.double_buffering,  "Buffered gfx:" },
1283 #endif
1284     { &setup.scroll_delay,      "Scroll Delay:" },
1285     { &setup.soft_scrolling,    "Soft Scroll.:" },
1286     { &setup.fading,            "Fading:"       },
1287     { &setup.quick_doors,       "Quick Doors:"  },
1288     { &setup.autorecord,        "Auto-Record:"  },
1289     { &setup.team_mode,         "Team-Mode:"    },
1290     { &setup.handicap,          "Handicap:"     },
1291     { &setup.time_limit,        "Timelimit:"    },
1292     { NULL,                     "Input Devices" },
1293     { NULL,                     ""              },
1294     { NULL,                     "Exit"          },
1295     { NULL,                     "Save and exit" }
1296   };
1297
1298   UnmapAllGadgets();
1299   CloseDoor(DOOR_CLOSE_2);
1300   ClearWindow();
1301
1302   DrawText(SX + 16, SY + 16, "SETUP",FS_BIG,FC_YELLOW);
1303
1304   for(i=SETUP_SCREEN_POS_START;i<=SETUP_SCREEN_POS_END;i++)
1305   {
1306     int base = i - SETUP_SCREEN_POS_START;
1307
1308     if (!(i >= SETUP_SCREEN_POS_EMPTY1 && i <= SETUP_SCREEN_POS_EMPTY2))
1309     {
1310       DrawText(SX+32,SY+i*32, setup_info[base].text, FS_BIG,FC_GREEN);
1311
1312       if (strcmp(setup_info[base].text, "Input Devices") == 0)
1313         initCursor(i, GFX_ARROW_BLUE_RIGHT);
1314       else
1315         initCursor(i, GFX_KUGEL_BLAU);
1316     }
1317
1318     if (setup_info[base].value)
1319     {
1320       int setting_value = *setup_info[base].value;
1321
1322       DrawText(SX+14*32, SY+i*32, (setting_value ? "on" : "off"),
1323                FS_BIG, (setting_value ? FC_YELLOW : FC_BLUE));
1324     }
1325   }
1326
1327   FadeToFront();
1328   InitAnimation();
1329   HandleSetupScreen(0,0,0,0,MB_MENU_INITIALIZE);
1330 }
1331
1332 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
1333 {
1334   static int choice = 3;
1335   static int redraw = TRUE;
1336   int x = (mx+32-SX)/32, y = (my+32-SY)/32;
1337   int pos_start  = SETUP_SCREEN_POS_START  + 1;
1338   int pos_empty1 = SETUP_SCREEN_POS_EMPTY1 + 1;
1339   int pos_empty2 = SETUP_SCREEN_POS_EMPTY2 + 1;
1340   int pos_end    = SETUP_SCREEN_POS_END    + 1;
1341
1342   if (button == MB_MENU_INITIALIZE)
1343     redraw = TRUE;
1344
1345   if (redraw)
1346   {
1347     drawCursor(choice - 1, FC_RED);
1348     redraw = FALSE;
1349   }
1350
1351   if (button == MB_MENU_INITIALIZE)
1352     return;
1353
1354   if (dx || dy)
1355   {
1356     if (dy)
1357     {
1358       x = 1;
1359       y = choice+dy;
1360     }
1361     else
1362       x = y = 0;
1363
1364     if (y >= pos_empty1 && y <= pos_empty2)
1365       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
1366
1367     if (y < pos_start)
1368       y = pos_start;
1369     else if (y > pos_end)
1370       y = pos_end;
1371   }
1372
1373   if (!mx && !my && !dx && !dy)
1374   {
1375     x = 1;
1376     y = choice;
1377   }
1378
1379   if (dx == 1 && choice == 14)
1380   {
1381     game_status = SETUPINPUT;
1382     DrawSetupInputScreen();
1383     redraw = TRUE;
1384   }
1385
1386   if (x==1 && y >= pos_start && y <= pos_end &&
1387       !(y >= pos_empty1 && y <= pos_empty2))
1388   {
1389     if (button)
1390     {
1391       if (y!=choice)
1392       {
1393         drawCursor(y - 1, FC_RED);
1394         drawCursor(choice - 1, FC_BLUE);
1395       }
1396       choice = y;
1397     }
1398     else
1399     {
1400       int yy = y-1;
1401
1402       if (y==3 && sound_status==SOUND_AVAILABLE)
1403       {
1404         if (setup.sound)
1405         {
1406           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1407           DrawText(SX+14*32, SY+(yy+1)*32,"off",FS_BIG,FC_BLUE);
1408           DrawText(SX+14*32, SY+(yy+2)*32,"off",FS_BIG,FC_BLUE);
1409           setup.sound_loops = FALSE;
1410           setup.sound_music = FALSE;
1411         }
1412         else
1413           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1414         setup.sound = !setup.sound;
1415       }
1416       else if (y==4 && sound_loops_allowed)
1417       {
1418         if (setup.sound_loops)
1419           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1420         else
1421         {
1422           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1423           DrawText(SX+14*32, SY+(yy-1)*32,"on ",FS_BIG,FC_YELLOW);
1424           setup.sound = TRUE;
1425         }
1426         setup.sound_loops = !setup.sound_loops;
1427       }
1428       else if (y==5 && sound_loops_allowed)
1429       {
1430         if (setup.sound_music)
1431           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1432         else
1433         {
1434           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1435           DrawText(SX+14*32, SY+(yy-2)*32,"on ",FS_BIG,FC_YELLOW);
1436           setup.sound = TRUE;
1437         }
1438         setup.sound_music = !setup.sound_music;
1439       }
1440
1441 #if 0
1442       else if (y==6)
1443       {
1444         if (setup.toons)
1445           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1446         else
1447           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1448         setup.toons = !setup.toons;
1449       }
1450       else if (y==7)
1451       {
1452 #if 0
1453         if (setup.double_buffering)
1454           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1455         else
1456           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1457         setup.double_buffering = !setup.double_buffering;
1458         setup.direct_draw = !setup.double_buffering;
1459 #else
1460         DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1461         setup.double_buffering = TRUE;
1462         setup.direct_draw = !setup.double_buffering;
1463 #endif
1464       }
1465 #endif
1466
1467       else if (y==6)
1468       {
1469         if (setup.scroll_delay)
1470           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1471         else
1472           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1473         setup.scroll_delay = !setup.scroll_delay;
1474       }
1475       else if (y==7)
1476       {
1477         if (setup.soft_scrolling)
1478           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1479         else
1480           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1481         setup.soft_scrolling = !setup.soft_scrolling;
1482       }
1483       else if (y==8)
1484       {
1485         if (setup.fading)
1486           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1487         else
1488           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1489         setup.fading = !setup.fading;
1490       }
1491       else if (y==9)
1492       {
1493         if (setup.quick_doors)
1494           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1495         else
1496           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1497         setup.quick_doors = !setup.quick_doors;
1498       }
1499       else if (y==10)
1500       {
1501         if (setup.autorecord)
1502           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1503         else
1504           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1505         setup.autorecord = !setup.autorecord;
1506       }
1507       else if (y==11)
1508       {
1509         if (setup.team_mode)
1510           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1511         else
1512           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1513         setup.team_mode = !setup.team_mode;
1514       }
1515       else if (y==12)
1516       {
1517         if (setup.handicap)
1518           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1519         else
1520           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1521         setup.handicap = !setup.handicap;
1522       }
1523       else if (y==13)
1524       {
1525         if (setup.time_limit)
1526           DrawText(SX+14*32, SY+yy*32,"off",FS_BIG,FC_BLUE);
1527         else
1528           DrawText(SX+14*32, SY+yy*32,"on ",FS_BIG,FC_YELLOW);
1529         setup.time_limit = !setup.time_limit;
1530       }
1531       else if (y==14)
1532       {
1533         game_status = SETUPINPUT;
1534         DrawSetupInputScreen();
1535         redraw = TRUE;
1536       }
1537       else if (y==pos_end-1 || y==pos_end)
1538       {
1539         if (y==pos_end)
1540         {
1541           SaveSetup();
1542
1543           /*
1544           SaveJoystickData();
1545           */
1546
1547 #ifdef MSDOS
1548           save_joystick_data(JOYSTICK_FILENAME);
1549 #endif
1550
1551
1552         }
1553
1554         game_status = MAINMENU;
1555         DrawMainMenu();
1556         redraw = TRUE;
1557       }
1558     }
1559   }
1560   BackToFront();
1561
1562   if (game_status==SETUP)
1563     DoAnimation();
1564 }
1565
1566 void DrawSetupInputScreen()
1567 {
1568   ClearWindow();
1569   DrawText(SX+16, SY+16, "SETUP INPUT", FS_BIG, FC_YELLOW);
1570
1571   initCursor(2, GFX_KUGEL_BLAU);
1572   initCursor(3, GFX_KUGEL_BLAU);
1573   initCursor(4, GFX_ARROW_BLUE_RIGHT);
1574   initCursor(15, GFX_KUGEL_BLAU);
1575
1576   DrawGraphic(10, 2, GFX_ARROW_BLUE_LEFT);
1577   DrawGraphic(12, 2, GFX_ARROW_BLUE_RIGHT);
1578
1579   DrawText(SX+32, SY+2*32, "Player:", FS_BIG, FC_GREEN);
1580   DrawText(SX+32, SY+3*32, "Device:", FS_BIG, FC_GREEN);
1581   DrawText(SX+32, SY+15*32, "Exit", FS_BIG, FC_GREEN);
1582
1583   DrawTextFCentered(SYSIZE - 20, FC_BLUE,
1584                     "Joysticks deactivated on this screen");
1585
1586   HandleSetupInputScreen(0,0, 0,0, MB_MENU_INITIALIZE);
1587   FadeToFront();
1588   InitAnimation();
1589 }
1590
1591 static void setJoystickDeviceToNr(char *device_name, int device_nr)
1592 {
1593   if (device_name == NULL)
1594     return;
1595
1596   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
1597     device_nr = 0;
1598
1599   if (strlen(device_name) > 1)
1600   {
1601     char c1 = device_name[strlen(device_name) - 1];
1602     char c2 = device_name[strlen(device_name) - 2];
1603
1604     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
1605       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
1606   }
1607   else
1608     strncpy(device_name, joystick_device_name[device_nr], strlen(device_name));
1609 }
1610
1611 static void drawPlayerSetupInputInfo(int player_nr)
1612 {
1613   int i;
1614   static struct SetupKeyboardInfo custom_key;
1615   static struct
1616   {
1617     Key *key;
1618     char *text;
1619   } custom[] =
1620   {
1621     { &custom_key.left,  "Joystick Left"  },
1622     { &custom_key.right, "Joystick Right" },
1623     { &custom_key.up,    "Joystick Up"    },
1624     { &custom_key.down,  "Joystick Down"  },
1625     { &custom_key.snap,  "Button 1"       },
1626     { &custom_key.bomb,  "Button 2"       }
1627   };
1628   static char *joystick_name[MAX_PLAYERS] =
1629   {
1630     "Joystick1",
1631     "Joystick2",
1632     "Joystick3",
1633     "Joystick4"
1634   };
1635
1636   custom_key = setup.input[player_nr].key;
1637
1638   DrawText(SX+11*32, SY+2*32, int2str(player_nr + 1, 1), FS_BIG, FC_RED);
1639   DrawGraphic(8, 2, GFX_SPIELER1 + player_nr);
1640
1641   if (setup.input[player_nr].use_joystick)
1642   {
1643     char *device_name = setup.input[player_nr].joy.device_name;
1644
1645     DrawText(SX+8*32, SY+3*32,
1646              joystick_name[getJoystickNrFromDeviceName(device_name)],
1647              FS_BIG, FC_YELLOW);
1648     DrawText(SX+32, SY+4*32, "Calibrate", FS_BIG, FC_GREEN);
1649   }
1650   else
1651   {
1652     DrawText(SX+8*32, SY+3*32, "Keyboard ", FS_BIG, FC_YELLOW);
1653     DrawText(SX+32, SY+4*32, "Customize", FS_BIG, FC_GREEN);
1654   }
1655
1656   DrawText(SX+32, SY+5*32, "Actual Settings:", FS_BIG, FC_GREEN);
1657   DrawGraphic(1, 6, GFX_ARROW_BLUE_LEFT);
1658   DrawGraphic(1, 7, GFX_ARROW_BLUE_RIGHT);
1659   DrawGraphic(1, 8, GFX_ARROW_BLUE_UP);
1660   DrawGraphic(1, 9, GFX_ARROW_BLUE_DOWN);
1661   DrawText(SX+2*32, SY+6*32, ":", FS_BIG, FC_BLUE);
1662   DrawText(SX+2*32, SY+7*32, ":", FS_BIG, FC_BLUE);
1663   DrawText(SX+2*32, SY+8*32, ":", FS_BIG, FC_BLUE);
1664   DrawText(SX+2*32, SY+9*32, ":", FS_BIG, FC_BLUE);
1665   DrawText(SX+32, SY+10*32, "Snap Field:", FS_BIG, FC_BLUE);
1666   DrawText(SX+32, SY+12*32, "Place Bomb:", FS_BIG, FC_BLUE);
1667
1668   for (i=0; i<6; i++)
1669   {
1670     int ypos = 6 + i + (i > 3 ? i-3 : 0);
1671
1672     DrawText(SX + 3*32, SY + ypos*32,
1673              "              ", FS_BIG, FC_YELLOW);
1674     DrawText(SX + 3*32, SY + ypos*32,
1675              (setup.input[player_nr].use_joystick ?
1676               custom[i].text :
1677               getKeyNameFromKey(*custom[i].key)),
1678              FS_BIG, FC_YELLOW);
1679   }
1680 }
1681
1682 void HandleSetupInputScreen(int mx, int my, int dx, int dy, int button)
1683 {
1684   static int choice = 3;
1685   static int player_nr = 0;
1686   static int redraw = TRUE;
1687   int x = (mx+32-SX)/32, y = (my+32-SY)/32;
1688   int pos_start  = SETUPINPUT_SCREEN_POS_START  + 1;
1689   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1 + 1;
1690   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2 + 1;
1691   int pos_end    = SETUPINPUT_SCREEN_POS_END    + 1;
1692
1693   if (button == MB_MENU_INITIALIZE)
1694   {
1695     drawPlayerSetupInputInfo(player_nr);
1696     redraw = TRUE;
1697   }
1698
1699   if (redraw)
1700   {
1701     drawCursor(choice - 1, FC_RED);
1702     redraw = FALSE;
1703   }
1704
1705   if (button == MB_MENU_INITIALIZE)
1706     return;
1707
1708   if (dx || dy)
1709   {
1710     if (dx && choice == 3)
1711     {
1712       x = (dx < 0 ? 11 : 13);
1713       y = 3;
1714     }
1715     else if (dx && choice == 4)
1716     {
1717       button = MB_MENU_CHOICE;
1718       x = 1;
1719       y = 4;
1720     }
1721     else if (dy)
1722     {
1723       x = 1;
1724       y = choice + dy;
1725     }
1726     else
1727       x = y = 0;
1728
1729     if (y >= pos_empty1 && y <= pos_empty2)
1730       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
1731
1732     if (y < pos_start)
1733       y = pos_start;
1734     else if (y > pos_end)
1735       y = pos_end;
1736   }
1737
1738   if (!mx && !my && !dx && !dy)
1739   {
1740     x = 1;
1741     y = choice;
1742   }
1743
1744   if (y == 3 && ((x == 1 && !button) || ((x == 11 || x == 13) && button)))
1745   {
1746     static unsigned long delay = 0;
1747
1748     if (!DelayReached(&delay, GADGET_FRAME_DELAY))
1749       goto out;
1750
1751     player_nr = (player_nr + (x == 11 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS;
1752
1753     drawPlayerSetupInputInfo(player_nr);
1754   }
1755   else if (x==1 && y >= pos_start && y <= pos_end &&
1756            !(y >= pos_empty1 && y <= pos_empty2))
1757   {
1758     if (button)
1759     {
1760       if (y != choice)
1761       {
1762         drawCursor(y - 1, FC_RED);
1763         drawCursor(choice - 1, FC_BLUE);
1764       }
1765       choice = y;
1766     }
1767     else
1768     {
1769       if (y == 4)
1770       {
1771         char *device_name = setup.input[player_nr].joy.device_name;
1772
1773         if (!setup.input[player_nr].use_joystick)
1774         {
1775           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
1776
1777           setJoystickDeviceToNr(device_name, new_device_nr);
1778           setup.input[player_nr].use_joystick = TRUE;
1779         }
1780         else
1781         {
1782           int device_nr = getJoystickNrFromDeviceName(device_name);
1783           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
1784
1785           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
1786             setup.input[player_nr].use_joystick = FALSE;
1787           else
1788             setJoystickDeviceToNr(device_name, new_device_nr);
1789         }
1790
1791
1792         /*
1793         InitJoysticks();
1794         */
1795
1796
1797 #if 0
1798         int one_joystick_nr       = (dx >= 0 ? 0 : 1);
1799         int the_other_joystick_nr = (dx >= 0 ? 1 : 0);
1800
1801         if (setup.input[player_nr].use_joystick)
1802         {
1803           if (setup.input[player_nr].joystick_nr == one_joystick_nr)
1804             setup.input[player_nr].joystick_nr = the_other_joystick_nr;
1805           else
1806             setup.input[player_nr].use_joystick = FALSE;
1807         }
1808         else
1809         {
1810           setup.input[player_nr].use_joystick = TRUE;
1811           setup.input[player_nr].joystick_nr = one_joystick_nr;
1812         }
1813 #endif
1814
1815         drawPlayerSetupInputInfo(player_nr);
1816       }
1817       else if (y == 5)
1818       {
1819         if (setup.input[player_nr].use_joystick)
1820         {
1821           InitJoysticks();
1822           game_status = CALIBRATION;
1823           CalibrateJoystick(player_nr);
1824           game_status = SETUPINPUT;
1825         }
1826         else
1827           CustomizeKeyboard(player_nr);
1828
1829         redraw = TRUE;
1830       }
1831       else if (y == pos_end)
1832       {
1833         InitJoysticks();
1834
1835         game_status = SETUP;
1836         DrawSetupScreen();
1837         redraw = TRUE;
1838       }
1839     }
1840   }
1841   BackToFront();
1842
1843   out:
1844
1845   if (game_status == SETUPINPUT)
1846     DoAnimation();
1847 }
1848
1849 void CustomizeKeyboard(int player_nr)
1850 {
1851   int i;
1852   int step_nr;
1853   boolean finished = FALSE;
1854   static struct SetupKeyboardInfo custom_key;
1855   static struct
1856   {
1857     Key *key;
1858     char *text;
1859   } customize_step[] =
1860   {
1861     { &custom_key.left,  "Move Left"  },
1862     { &custom_key.right, "Move Right" },
1863     { &custom_key.up,    "Move Up"    },
1864     { &custom_key.down,  "Move Down"  },
1865     { &custom_key.snap,  "Snap Field" },
1866     { &custom_key.bomb,  "Place Bomb" }
1867   };
1868
1869   /* read existing key bindings from player setup */
1870   custom_key = setup.input[player_nr].key;
1871
1872   ClearWindow();
1873   DrawText(SX + 16, SY + 16, "Keyboard Input", FS_BIG, FC_YELLOW);
1874
1875   BackToFront();
1876   InitAnimation();
1877
1878   step_nr = 0;
1879   DrawText(SX, SY + (2+2*step_nr)*32,
1880            customize_step[step_nr].text, FS_BIG, FC_RED);
1881   DrawText(SX, SY + (2+2*step_nr+1)*32,
1882            "Key:", FS_BIG, FC_RED);
1883   DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
1884            getKeyNameFromKey(*customize_step[step_nr].key),
1885            FS_BIG, FC_BLUE);
1886
1887   while(!finished)
1888   {
1889     if (PendingEvent())         /* got event */
1890     {
1891       Event event;
1892
1893       NextEvent(&event);
1894
1895       switch(event.type)
1896       {
1897         case EVENT_KEYPRESS:
1898           {
1899             Key key = GetEventKey((KeyEvent *)&event, TRUE);
1900
1901             if (key == KEY_Escape || (key == KEY_Return && step_nr == 6))
1902             {
1903               finished = TRUE;
1904               break;
1905             }
1906
1907             /* all keys configured -- wait for "Escape" or "Return" key */
1908             if (step_nr == 6)
1909               break;
1910
1911             /* press 'Enter' to keep the existing key binding */
1912             if (key == KEY_Return)
1913               key = *customize_step[step_nr].key;
1914
1915             /* check if key already used */
1916             for (i=0; i<step_nr; i++)
1917               if (*customize_step[i].key == key)
1918                 break;
1919             if (i < step_nr)
1920               break;
1921
1922             /* got new key binding */
1923             *customize_step[step_nr].key = key;
1924             DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
1925                      "             ", FS_BIG, FC_YELLOW);
1926             DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32,
1927                      getKeyNameFromKey(key), FS_BIG, FC_YELLOW);
1928             step_nr++;
1929
1930             /* un-highlight last query */
1931             DrawText(SX, SY+(2+2*(step_nr-1))*32,
1932                      customize_step[step_nr-1].text, FS_BIG, FC_GREEN);
1933             DrawText(SX, SY+(2+2*(step_nr-1)+1)*32,
1934                      "Key:", FS_BIG, FC_GREEN);
1935
1936             /* press 'Enter' to leave */
1937             if (step_nr == 6)
1938             {
1939               DrawText(SX + 16, SY + 15*32+16,
1940                        "Press Enter", FS_BIG, FC_YELLOW);
1941               break;
1942             }
1943
1944             /* query next key binding */
1945             DrawText(SX, SY+(2+2*step_nr)*32,
1946                      customize_step[step_nr].text, FS_BIG, FC_RED);
1947             DrawText(SX, SY+(2+2*step_nr+1)*32,
1948                      "Key:", FS_BIG, FC_RED);
1949             DrawText(SX + 4*32, SY+(2+2*step_nr+1)*32,
1950                      getKeyNameFromKey(*customize_step[step_nr].key),
1951                      FS_BIG, FC_BLUE);
1952           }
1953           break;
1954
1955         case EVENT_KEYRELEASE:
1956           key_joystick_mapping = 0;
1957           break;
1958
1959         default:
1960           HandleOtherEvents(&event);
1961           break;
1962       }
1963     }
1964
1965     BackToFront();
1966     DoAnimation();
1967
1968     /* don't eat all CPU time */
1969     Delay(10);
1970   }
1971
1972   /* write new key bindings back to player setup */
1973   setup.input[player_nr].key = custom_key;
1974
1975   StopAnimation();
1976   DrawSetupInputScreen();
1977 }
1978
1979 void CalibrateJoystick(int player_nr)
1980 {
1981 #ifdef __FreeBSD__
1982   struct joystick joy_ctrl;
1983 #else
1984   struct joystick_control
1985   {
1986     int buttons;
1987     int x;
1988     int y;
1989   } joy_ctrl;
1990 #endif
1991
1992 #ifndef MSDOS
1993   int new_joystick_xleft = 128, new_joystick_xright = 128;
1994   int new_joystick_yupper = 128, new_joystick_ylower = 128;
1995   int new_joystick_xmiddle, new_joystick_ymiddle;
1996 #else
1997   int calibration_step = 1;
1998 #endif
1999
2000   int joystick_fd = stored_player[player_nr].joystick_fd;
2001   int x, y, last_x, last_y, xpos = 8, ypos = 3;
2002   boolean check[3][3];
2003   int check_remaining;
2004   int joy_value;
2005   int result = -1;
2006
2007   if (joystick_status == JOYSTICK_OFF ||
2008       joystick_fd < 0 ||
2009       !setup.input[player_nr].use_joystick)
2010     goto error_out;
2011
2012   ClearWindow();
2013
2014 #ifndef MSDOS
2015   DrawText(SX,      SY +  6*32, " ROTATE JOYSTICK ", FS_BIG, FC_YELLOW);
2016   DrawText(SX,      SY +  7*32, "IN ALL DIRECTIONS", FS_BIG, FC_YELLOW);
2017   DrawText(SX + 16, SY +  9*32, "  IF ALL BALLS  ",  FS_BIG, FC_YELLOW);
2018   DrawText(SX,      SY + 10*32, "   ARE YELLOW,   ", FS_BIG, FC_YELLOW);
2019   DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
2020   check_remaining = 3 * 3;
2021 #else
2022   DrawText(SX,      SY +  7*32, "  MOVE JOYSTICK  ", FS_BIG, FC_YELLOW);
2023   DrawText(SX + 16, SY +  8*32, "       TO       ",  FS_BIG, FC_YELLOW);
2024   DrawText(SX,      SY +  9*32, " CENTER POSITION ",  FS_BIG, FC_YELLOW);
2025   DrawText(SX,      SY + 10*32, "       AND       ", FS_BIG, FC_YELLOW);
2026   DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
2027   check_remaining = 0;
2028 #endif
2029
2030   for(y=0; y<3; y++)
2031   {
2032     for(x=0; x<3; x++)
2033     {
2034       check[x][y] = FALSE;
2035       DrawGraphic(xpos + x - 1, ypos + y - 1, GFX_KUGEL_BLAU);
2036     }
2037   }
2038
2039   joy_value = Joystick(player_nr);
2040   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
2041   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
2042   DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_ROT);
2043
2044   BackToFront();
2045
2046 #ifdef __FreeBSD__
2047   joy_ctrl.b1 = joy_ctrl.b2 = 0;
2048 #else
2049   joy_ctrl.buttons = 0;
2050 #endif
2051
2052   while(Joystick(player_nr) & JOY_BUTTON);
2053
2054   InitAnimation();
2055
2056   while(result < 0)
2057   {
2058     if (PendingEvent())         /* got event */
2059     {
2060       Event event;
2061
2062       NextEvent(&event);
2063
2064       switch(event.type)
2065       {
2066         case EVENT_KEYPRESS:
2067           switch(GetEventKey((KeyEvent *)&event, TRUE))
2068           {
2069             case KEY_Return:
2070               if (check_remaining == 0)
2071                 result = 1;
2072               break;
2073
2074             case KEY_Escape:
2075               result = 0;
2076               break;
2077
2078             default:
2079               break;
2080           }
2081           break;
2082
2083         case EVENT_KEYRELEASE:
2084           key_joystick_mapping = 0;
2085           break;
2086
2087         default:
2088           HandleOtherEvents(&event);
2089           break;
2090       }
2091     }
2092
2093 #ifndef MSDOS
2094
2095 #ifdef USE_SDL_LIBRARY
2096     joy_ctrl.x = Get_SDL_Joystick_Axis(joystick_fd, 0);
2097     joy_ctrl.y = Get_SDL_Joystick_Axis(joystick_fd, 1);
2098 #else
2099     if (read(joystick_fd, &joy_ctrl, sizeof(joy_ctrl)) != sizeof(joy_ctrl))
2100     {
2101       joystick_status = JOYSTICK_OFF;
2102       goto error_out;
2103     }
2104 #endif
2105
2106     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_ctrl.x);
2107     new_joystick_xright = MAX(new_joystick_xright, joy_ctrl.x);
2108     new_joystick_yupper = MIN(new_joystick_yupper, joy_ctrl.y);
2109     new_joystick_ylower = MAX(new_joystick_ylower, joy_ctrl.y);
2110
2111     new_joystick_xmiddle =
2112       new_joystick_xleft + (new_joystick_xright - new_joystick_xleft) / 2;
2113     new_joystick_ymiddle =
2114       new_joystick_yupper + (new_joystick_ylower - new_joystick_yupper) / 2;
2115
2116     setup.input[player_nr].joy.xleft = new_joystick_xleft;
2117     setup.input[player_nr].joy.yupper = new_joystick_yupper;
2118     setup.input[player_nr].joy.xright = new_joystick_xright;
2119     setup.input[player_nr].joy.ylower = new_joystick_ylower;
2120     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
2121     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
2122
2123     CheckJoystickData();
2124 #endif
2125
2126     joy_value = Joystick(player_nr);
2127
2128     if (joy_value & JOY_BUTTON && check_remaining == 0)
2129     {
2130       result = 1;
2131
2132 #ifdef MSDOS
2133       if (calibration_step == 1)
2134       {
2135         remove_joystick();
2136         InitJoysticks();
2137       }
2138       else if (calibrate_joystick(joystick_fd) != 0)
2139       {
2140         joystick_status = JOYSTICK_OFF;
2141         goto error_out;
2142       }
2143
2144       if (joy[joystick_fd].flags & JOYFLAG_CALIBRATE)
2145       {
2146         calibration_step++;
2147         result = -1;
2148
2149         DrawText(SX,      SY +  7*32, "  MOVE JOYSTICK  ", FS_BIG, FC_YELLOW);
2150         DrawText(SX + 16, SY +  8*32, "       TO       ",  FS_BIG, FC_YELLOW);
2151
2152         if (calibration_step == 2)
2153           DrawText(SX + 16, SY + 9*32," THE UPPER LEFT ",  FS_BIG, FC_YELLOW);
2154         else
2155           DrawText(SX,      SY + 9*32," THE LOWER RIGHT ", FS_BIG, FC_YELLOW);
2156
2157         DrawText(SX,      SY + 10*32, "       AND       ", FS_BIG, FC_YELLOW);
2158         DrawText(SX,      SY + 11*32, "PRESS ANY BUTTON!", FS_BIG, FC_YELLOW);
2159
2160         BackToFront();
2161
2162         while(Joystick(player_nr) & JOY_BUTTON)
2163           DoAnimation();
2164       }
2165 #endif
2166     }
2167
2168     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
2169     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
2170
2171     if (x != last_x || y != last_y)
2172     {
2173 #ifndef MSDOS
2174       DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_GELB);
2175 #else
2176       DrawGraphic(xpos + last_x, ypos + last_y, GFX_KUGEL_BLAU);
2177 #endif
2178       DrawGraphic(xpos + x,      ypos + y,      GFX_KUGEL_ROT);
2179
2180       last_x = x;
2181       last_y = y;
2182
2183       if (check_remaining > 0 && !check[x+1][y+1])
2184       {
2185         check[x+1][y+1] = TRUE;
2186         check_remaining--;
2187       }
2188
2189 #if 0
2190       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
2191              setup.input[player_nr].joy.xleft,
2192              setup.input[player_nr].joy.xmiddle,
2193              setup.input[player_nr].joy.xright);
2194       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
2195              setup.input[player_nr].joy.yupper,
2196              setup.input[player_nr].joy.ymiddle,
2197              setup.input[player_nr].joy.ylower);
2198 #endif
2199
2200     }
2201
2202     BackToFront();
2203     DoAnimation();
2204
2205     /* don't eat all CPU time */
2206     Delay(10);
2207   }
2208
2209   StopAnimation();
2210
2211   DrawSetupInputScreen();
2212
2213   /* wait until the last pressed button was released */
2214   while(Joystick(player_nr) & JOY_BUTTON)
2215   {
2216     if (PendingEvent())         /* got event */
2217     {
2218       Event event;
2219
2220       NextEvent(&event);
2221       HandleOtherEvents(&event);
2222
2223       Delay(10);
2224     }
2225   }
2226   return;
2227
2228   error_out:
2229
2230   ClearWindow();
2231   DrawText(SX + 16, SY + 6*32, "  JOYSTICK NOT  ", FS_BIG, FC_YELLOW);
2232   DrawText(SX,      SY + 7*32, "    AVAILABLE    ", FS_BIG, FC_YELLOW);
2233   BackToFront();
2234   Delay(2000);
2235   DrawSetupInputScreen();
2236 }
2237
2238 void HandleGameActions()
2239 {
2240   if (game_status != PLAYING)
2241     return;
2242
2243   if (local_player->LevelSolved)
2244     GameWon();
2245
2246   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
2247     TapeStop();
2248
2249   GameActions();
2250
2251   BackToFront();
2252 }
2253
2254 /* ---------- new screen button stuff -------------------------------------- */
2255
2256 /* graphic position and size values for buttons and scrollbars */
2257 #define SC_SCROLLBUTTON_XPOS            64
2258 #define SC_SCROLLBUTTON_YPOS            0
2259 #define SC_SCROLLBAR_XPOS               0
2260 #define SC_SCROLLBAR_YPOS               64
2261
2262 #define SC_SCROLLBUTTON_XSIZE           32
2263 #define SC_SCROLLBUTTON_YSIZE           32
2264
2265 #define SC_SCROLL_UP_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
2266 #define SC_SCROLL_UP_YPOS               SC_SCROLLBUTTON_YSIZE
2267 #define SC_SCROLL_DOWN_XPOS             SC_SCROLL_UP_XPOS
2268 #define SC_SCROLL_DOWN_YPOS             (SYSIZE - SC_SCROLLBUTTON_YSIZE)
2269 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLL_UP_XPOS
2270 #define SC_SCROLL_VERTICAL_YPOS   (SC_SCROLL_UP_YPOS + SC_SCROLLBUTTON_YSIZE)
2271 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
2272 #define SC_SCROLL_VERTICAL_YSIZE        (SYSIZE - 3 * SC_SCROLLBUTTON_YSIZE)
2273
2274 #define SC_BORDER_SIZE                  14
2275
2276 static struct
2277 {
2278   int xpos, ypos;
2279   int x, y;
2280   int gadget_id;
2281   char *infotext;
2282 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
2283 {
2284   {
2285     SC_SCROLLBUTTON_XPOS + 0 * SC_SCROLLBUTTON_XSIZE,   SC_SCROLLBUTTON_YPOS,
2286     SC_SCROLL_UP_XPOS,                                  SC_SCROLL_UP_YPOS,
2287     SCREEN_CTRL_ID_SCROLL_UP,
2288     "scroll level series up"
2289   },
2290   {
2291     SC_SCROLLBUTTON_XPOS + 1 * SC_SCROLLBUTTON_XSIZE,   SC_SCROLLBUTTON_YPOS,
2292     SC_SCROLL_DOWN_XPOS,                                SC_SCROLL_DOWN_YPOS,
2293     SCREEN_CTRL_ID_SCROLL_DOWN,
2294     "scroll level series down"
2295   }
2296 };
2297
2298 static struct
2299 {
2300   int xpos, ypos;
2301   int x, y;
2302   int width, height;
2303   int type;
2304   int gadget_id;
2305   char *infotext;
2306 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
2307 {
2308   {
2309     SC_SCROLLBAR_XPOS,                  SC_SCROLLBAR_YPOS,
2310     SX + SC_SCROLL_VERTICAL_XPOS,       SY + SC_SCROLL_VERTICAL_YPOS,
2311     SC_SCROLL_VERTICAL_XSIZE,           SC_SCROLL_VERTICAL_YSIZE,
2312     GD_TYPE_SCROLLBAR_VERTICAL,
2313     SCREEN_CTRL_ID_SCROLL_VERTICAL,
2314     "scroll level series vertically"
2315   }
2316 };
2317
2318 static void CreateScreenScrollbuttons()
2319 {
2320   Bitmap gd_bitmap = pix[PIX_MORE];
2321   struct GadgetInfo *gi;
2322   unsigned long event_mask;
2323   int i;
2324
2325   for (i=0; i<NUM_SCREEN_SCROLLBUTTONS; i++)
2326   {
2327     int id = scrollbutton_info[i].gadget_id;
2328     int x, y, width, height;
2329     int gd_x1, gd_x2, gd_y1, gd_y2;
2330
2331     x = scrollbutton_info[i].x;
2332     y = scrollbutton_info[i].y;
2333
2334     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
2335
2336     x += SX;
2337     y += SY;
2338     width = SC_SCROLLBUTTON_XSIZE;
2339     height = SC_SCROLLBUTTON_YSIZE;
2340     gd_x1 = scrollbutton_info[i].xpos;
2341     gd_y1 = scrollbutton_info[i].ypos;
2342     gd_x2 = gd_x1;
2343     gd_y2 = gd_y1 + SC_SCROLLBUTTON_YSIZE;
2344
2345     gi = CreateGadget(GDI_CUSTOM_ID, id,
2346                       GDI_CUSTOM_TYPE_ID, i,
2347                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
2348                       GDI_X, x,
2349                       GDI_Y, y,
2350                       GDI_WIDTH, width,
2351                       GDI_HEIGHT, height,
2352                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
2353                       GDI_STATE, GD_BUTTON_UNPRESSED,
2354                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
2355                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
2356                       GDI_EVENT_MASK, event_mask,
2357                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
2358                       GDI_END);
2359
2360     if (gi == NULL)
2361       Error(ERR_EXIT, "cannot create gadget");
2362
2363     screen_gadget[id] = gi;
2364   }
2365 }
2366
2367 static void CreateScreenScrollbars()
2368 {
2369   int i;
2370
2371   for (i=0; i<NUM_SCREEN_SCROLLBARS; i++)
2372   {
2373     int id = scrollbar_info[i].gadget_id;
2374     Bitmap gd_bitmap = pix[PIX_MORE];
2375     int gd_x1, gd_x2, gd_y1, gd_y2;
2376     struct GadgetInfo *gi;
2377     int items_max, items_visible, item_position;
2378     unsigned long event_mask;
2379     int num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
2380
2381 #if 0
2382     if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
2383       num_page_entries = num_leveldirs;
2384     else
2385       num_page_entries = MAX_LEVEL_SERIES_ON_SCREEN - 1;
2386
2387     items_max = MAX(num_leveldirs, num_page_entries);
2388     items_visible = num_page_entries;
2389     item_position = 0;
2390 #else
2391     items_max = num_page_entries;
2392     items_visible = num_page_entries;
2393     item_position = 0;
2394 #endif
2395
2396     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
2397
2398     gd_x1 = scrollbar_info[i].xpos;
2399     gd_x2 = gd_x1 + scrollbar_info[i].width;
2400     gd_y1 = scrollbar_info[i].ypos;
2401     gd_y2 = scrollbar_info[i].ypos;
2402
2403     gi = CreateGadget(GDI_CUSTOM_ID, id,
2404                       GDI_CUSTOM_TYPE_ID, i,
2405                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
2406                       GDI_X, scrollbar_info[i].x,
2407                       GDI_Y, scrollbar_info[i].y,
2408                       GDI_WIDTH, scrollbar_info[i].width,
2409                       GDI_HEIGHT, scrollbar_info[i].height,
2410                       GDI_TYPE, scrollbar_info[i].type,
2411                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
2412                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
2413                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
2414                       GDI_STATE, GD_BUTTON_UNPRESSED,
2415                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
2416                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
2417                       GDI_BORDER_SIZE, SC_BORDER_SIZE,
2418                       GDI_EVENT_MASK, event_mask,
2419                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
2420                       GDI_END);
2421
2422     if (gi == NULL)
2423       Error(ERR_EXIT, "cannot create gadget");
2424
2425     screen_gadget[id] = gi;
2426   }
2427 }
2428
2429 void CreateScreenGadgets()
2430 {
2431   CreateScreenScrollbuttons();
2432   CreateScreenScrollbars();
2433 }
2434
2435 void MapChooseLevelGadgets()
2436 {
2437   int num_leveldirs = numLevelDirInfoInGroup(leveldir_current);
2438   int i;
2439
2440   if (num_leveldirs <= MAX_LEVEL_SERIES_ON_SCREEN)
2441     return;
2442
2443   for (i=0; i<NUM_SCREEN_GADGETS; i++)
2444     MapGadget(screen_gadget[i]);
2445 }
2446
2447 void UnmapChooseLevelGadgets()
2448 {
2449   int i;
2450
2451   for (i=0; i<NUM_SCREEN_GADGETS; i++)
2452     UnmapGadget(screen_gadget[i]);
2453 }
2454
2455 static void HandleScreenGadgets(struct GadgetInfo *gi)
2456 {
2457   int id = gi->custom_id;
2458
2459   if (game_status != CHOOSELEVEL)
2460     return;
2461
2462   switch (id)
2463   {
2464     case SCREEN_CTRL_ID_SCROLL_UP:
2465       HandleChooseLevel(SX,SY + 32, 0,0, MB_MENU_MARK);
2466       break;
2467
2468     case SCREEN_CTRL_ID_SCROLL_DOWN:
2469       HandleChooseLevel(SX,SY + SYSIZE - 32, 0,0, MB_MENU_MARK);
2470       break;
2471
2472     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
2473       HandleChooseLevel(0,0, 999,gi->event.item_position, MB_MENU_INITIALIZE);
2474       break;
2475
2476     default:
2477       break;
2478   }
2479 }