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