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