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