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