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