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