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