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