eb785c54eef1198420aae71f28788a07c417dddf
[rocksndiamonds.git] / src / screens.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 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_1           4
33 #define SETUP_MODE_SHORTCUT_2           5
34 #define SETUP_MODE_GRAPHICS             6
35 #define SETUP_MODE_CHOOSE_SCREEN_MODE   7
36 #define SETUP_MODE_SOUND                8
37 #define SETUP_MODE_ARTWORK              9
38 #define SETUP_MODE_CHOOSE_GRAPHICS      10
39 #define SETUP_MODE_CHOOSE_SOUNDS        11
40 #define SETUP_MODE_CHOOSE_MUSIC         12
41
42 #define MAX_SETUP_MODES                 13
43
44 /* for input setup functions */
45 #define SETUPINPUT_SCREEN_POS_START     0
46 #define SETUPINPUT_SCREEN_POS_END       (SCR_FIELDY - 4)
47 #define SETUPINPUT_SCREEN_POS_EMPTY1    (SETUPINPUT_SCREEN_POS_START + 3)
48 #define SETUPINPUT_SCREEN_POS_EMPTY2    (SETUPINPUT_SCREEN_POS_END - 1)
49
50 /* screens on the info screen */
51 #define INFO_MODE_MAIN                  0
52 #define INFO_MODE_TITLE                 1
53 #define INFO_MODE_ELEMENTS              2
54 #define INFO_MODE_MUSIC                 3
55 #define INFO_MODE_CREDITS               4
56 #define INFO_MODE_PROGRAM               5
57 #define INFO_MODE_LEVELSET              6
58
59 #define MAX_INFO_MODES                  7
60
61 /* for various menu stuff  */
62 #define MENU_SCREEN_START_XPOS          1
63 #define MENU_SCREEN_START_YPOS          2
64 #define MENU_SCREEN_VALUE_XPOS          14
65 #define MENU_SCREEN_MAX_XPOS            (SCR_FIELDX - 1)
66 #define MENU_TITLE1_YPOS                8
67 #define MENU_TITLE2_YPOS                46
68 #define MAX_INFO_ELEMENTS_ON_SCREEN     10
69 #define MAX_MENU_ENTRIES_ON_SCREEN      (SCR_FIELDY - MENU_SCREEN_START_YPOS)
70 #define MAX_MENU_TEXT_LENGTH_BIG        (MENU_SCREEN_VALUE_XPOS -       \
71                                          MENU_SCREEN_START_XPOS)
72 #define MAX_MENU_TEXT_LENGTH_MEDIUM     (MAX_MENU_TEXT_LENGTH_BIG * 2)
73
74 /* buttons and scrollbars identifiers */
75 #define SCREEN_CTRL_ID_PREV_LEVEL       0
76 #define SCREEN_CTRL_ID_NEXT_LEVEL       1
77 #define SCREEN_CTRL_ID_PREV_PLAYER      2
78 #define SCREEN_CTRL_ID_NEXT_PLAYER      3
79 #define SCREEN_CTRL_ID_SCROLL_UP        4
80 #define SCREEN_CTRL_ID_SCROLL_DOWN      5
81 #define SCREEN_CTRL_ID_SCROLL_VERTICAL  6
82
83 #define NUM_SCREEN_GADGETS              7
84
85 #define NUM_SCREEN_MENUBUTTONS          4
86 #define NUM_SCREEN_SCROLLBUTTONS        2
87 #define NUM_SCREEN_SCROLLBARS           1
88
89 #define SCREEN_MASK_MAIN                (1 << 0)
90 #define SCREEN_MASK_INPUT               (1 << 1)
91
92 /* graphic position and size values for buttons and scrollbars */
93 #define SC_MENUBUTTON_XSIZE             TILEX
94 #define SC_MENUBUTTON_YSIZE             TILEY
95
96 #define SC_SCROLLBUTTON_XSIZE           TILEX
97 #define SC_SCROLLBUTTON_YSIZE           TILEY
98
99 #define SC_SCROLLBAR_XPOS               (SXSIZE - SC_SCROLLBUTTON_XSIZE)
100
101 #define SC_SCROLL_VERTICAL_XSIZE        SC_SCROLLBUTTON_XSIZE
102 #define SC_SCROLL_VERTICAL_YSIZE        ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \
103                                          SC_SCROLLBUTTON_YSIZE)
104
105 #define SC_SCROLL_UP_XPOS               SC_SCROLLBAR_XPOS
106 #define SC_SCROLL_UP_YPOS               (2 * SC_SCROLLBUTTON_YSIZE)
107
108 #define SC_SCROLL_VERTICAL_XPOS         SC_SCROLLBAR_XPOS
109 #define SC_SCROLL_VERTICAL_YPOS         (SC_SCROLL_UP_YPOS + \
110                                          SC_SCROLLBUTTON_YSIZE)
111
112 #define SC_SCROLL_DOWN_XPOS             SC_SCROLLBAR_XPOS
113 #define SC_SCROLL_DOWN_YPOS             (SC_SCROLL_VERTICAL_YPOS + \
114                                          SC_SCROLL_VERTICAL_YSIZE)
115
116 #define SC_BORDER_SIZE                  14
117
118 /* other useful macro definitions */
119 #define BUTTON_GRAPHIC_ACTIVE(g)                                               \
120         (g == IMG_MENU_BUTTON_LEFT       ? IMG_MENU_BUTTON_LEFT_ACTIVE       : \
121          g == IMG_MENU_BUTTON_RIGHT      ? IMG_MENU_BUTTON_RIGHT_ACTIVE      : \
122          g == IMG_MENU_BUTTON_UP         ? IMG_MENU_BUTTON_UP_ACTIVE         : \
123          g == IMG_MENU_BUTTON_DOWN       ? IMG_MENU_BUTTON_DOWN_ACTIVE       : \
124          g == IMG_MENU_BUTTON_LEAVE_MENU ? IMG_MENU_BUTTON_LEAVE_MENU_ACTIVE : \
125          g == IMG_MENU_BUTTON_ENTER_MENU ? IMG_MENU_BUTTON_ENTER_MENU_ACTIVE : \
126          g == IMG_MENU_BUTTON_PREV_LEVEL ? IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE : \
127          g == IMG_MENU_BUTTON_NEXT_LEVEL ? IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE : \
128          IMG_MENU_BUTTON_ACTIVE)
129
130
131 /* forward declarations of internal functions */
132 static void HandleScreenGadgets(struct GadgetInfo *);
133 static void HandleSetupScreen_Generic(int, int, int, int, int);
134 static void HandleSetupScreen_Input(int, int, int, int, int);
135 static void CustomizeKeyboard(int);
136 static void CalibrateJoystick(int);
137 static void execSetupGraphics(void);
138 static void execSetupArtwork(void);
139 static void HandleChooseTree(int, int, int, int, int, TreeInfo **);
140
141 static void DrawChooseLevel(void);
142 static void DrawInfoScreen(void);
143 static void DrawAndFadeInInfoScreen(int);
144 static void DrawSetupScreen(void);
145
146 static void DrawInfoScreenExt(int, int);
147 static void DrawInfoScreen_NotAvailable(char *, char *);
148 static void DrawInfoScreen_HelpAnim(int, int, boolean);
149 static void DrawInfoScreen_HelpText(int, int, int, int);
150 static void HandleInfoScreen_Main(int, int, int, int, int);
151 static void HandleInfoScreen_TitleScreen(int);
152 static void HandleInfoScreen_Elements(int);
153 static void HandleInfoScreen_Music(int);
154 static void HandleInfoScreen_Credits(int);
155 static void HandleInfoScreen_Program(int);
156
157 static void MapScreenMenuGadgets(int);
158 static void MapScreenTreeGadgets(TreeInfo *);
159
160 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
161 static int setup_mode = SETUP_MODE_MAIN;
162 static int info_mode = INFO_MODE_MAIN;
163
164 static TreeInfo *screen_modes = NULL;
165 static TreeInfo *screen_mode_current = NULL;
166
167 #define DRAW_MODE(s)            ((s) >= GAME_MODE_MAIN &&               \
168                                  (s) <= GAME_MODE_SETUP ? (s) :         \
169                                  (s) == GAME_MODE_PSEUDO_TYPENAME ?     \
170                                  GAME_MODE_MAIN : GAME_MODE_DEFAULT)
171
172 #define DRAW_MODE_INFO(i)       ((i) >= INFO_MODE_ELEMENTS &&           \
173                                  (i) <= INFO_MODE_LEVELSET ? (i) :      \
174                                  INFO_MODE_MAIN)
175
176 #define DRAW_XOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?  \
177                                  menu.draw_xoffset[GAME_MODE_INFO] :    \
178                                  menu.draw_xoffset_info[DRAW_MODE_INFO(i)])
179 #define DRAW_YOFFSET_INFO(i)    (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ?  \
180                                  menu.draw_yoffset[GAME_MODE_INFO] :    \
181                                  menu.draw_yoffset_info[DRAW_MODE_INFO(i)])
182
183 #define DRAW_XOFFSET(s)         ((s) == GAME_MODE_INFO ?                \
184                                  DRAW_XOFFSET_INFO(info_mode) :         \
185                                  menu.draw_xoffset[DRAW_MODE(s)])
186 #define DRAW_YOFFSET(s)         ((s) == GAME_MODE_INFO ?                \
187                                  DRAW_YOFFSET_INFO(info_mode) :         \
188                                  menu.draw_yoffset[DRAW_MODE(s)])
189
190 #define mSX                     (SX + DRAW_XOFFSET(game_status))
191 #define mSY                     (SY + DRAW_YOFFSET(game_status))
192
193 #define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ?   \
194                                     menu.list_size[game_status] :       \
195                                     MAX_MENU_ENTRIES_ON_SCREEN)
196
197 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
198 #define NUM_SCROLLBAR_BITMAPS           2
199 static Bitmap *scrollbar_bitmap[NUM_SCROLLBAR_BITMAPS];
200 #endif
201
202
203 #define MAIN_CONTROL_NAME               0
204 #define MAIN_CONTROL_LEVELS             1
205 #define MAIN_CONTROL_SCORES             2
206 #define MAIN_CONTROL_EDITOR             3
207 #define MAIN_CONTROL_INFO               4
208 #define MAIN_CONTROL_GAME               5
209 #define MAIN_CONTROL_SETUP              6
210 #define MAIN_CONTROL_QUIT               7
211 #define MAIN_CONTROL_PREV_LEVEL         8
212 #define MAIN_CONTROL_NEXT_LEVEL         9
213 #define MAIN_CONTROL_CURRENT_LEVEL      10
214 #define MAIN_CONTROL_FIRST_LEVEL        11
215 #define MAIN_CONTROL_LAST_LEVEL         12
216 #define MAIN_CONTROL_LEVELSET_INFO      13
217 #define MAIN_CONTROL_LEVEL_INFO         14
218
219 static char main_text_name[10];
220 static char main_text_current_level[10];
221 static char main_text_first_level[10];
222 static char main_text_last_level[10];
223 static char main_input_name[MAX_PLAYER_NAME_LEN + 1];
224
225 static struct
226 {
227   int nr;
228
229   struct Rect *pos_button;
230   int button_graphic;
231
232   struct Rect *pos_text;
233   char *text;
234   int font_text;
235
236   struct Rect *pos_input;
237   char *input;
238   int font_input;
239 }
240 main_controls[] =
241 {
242   {
243     MAIN_CONTROL_NAME,
244     &menu.main.button.name,             IMG_MENU_BUTTON,
245     &menu.main.text.name,               main_text_name,         FONT_MENU_1,
246     &menu.main.input.name,              main_input_name,        FONT_INPUT_1,
247   },
248   {
249     MAIN_CONTROL_LEVELS,
250     &menu.main.button.levels,           IMG_MENU_BUTTON_ENTER_MENU,
251     &menu.main.text.levels,             "Levelset",             FONT_MENU_1,
252     NULL,                               NULL,                   -1,
253   },
254   {
255     MAIN_CONTROL_SCORES,
256     &menu.main.button.scores,           IMG_MENU_BUTTON,
257     &menu.main.text.scores,             "Hall Of Fame",         FONT_MENU_1,
258     NULL,                               NULL,                   -1,
259   },
260   {
261     MAIN_CONTROL_EDITOR,
262     &menu.main.button.editor,           IMG_MENU_BUTTON,
263     &menu.main.text.editor,             "Level Creator",        FONT_MENU_1,    
264     NULL,                               NULL,                   -1,
265   },
266   {
267     MAIN_CONTROL_INFO,
268     &menu.main.button.info,             IMG_MENU_BUTTON_ENTER_MENU,
269     &menu.main.text.info,               "Info Screen",          FONT_MENU_1,
270     NULL,                               NULL,                   -1,
271   },
272   {
273     MAIN_CONTROL_GAME,
274     &menu.main.button.game,             IMG_MENU_BUTTON,
275     &menu.main.text.game,               "Start Game",           FONT_MENU_1,
276     NULL,                               NULL,                   -1,
277   },
278   {
279     MAIN_CONTROL_SETUP,
280     &menu.main.button.setup,            IMG_MENU_BUTTON_ENTER_MENU,
281     &menu.main.text.setup,              "Setup",                FONT_MENU_1,
282     NULL,                               NULL,                   -1,
283   },
284   {
285     MAIN_CONTROL_QUIT,
286     &menu.main.button.quit,             IMG_MENU_BUTTON,
287     &menu.main.text.quit,               "Quit",                 FONT_MENU_1,
288     NULL,                               NULL,                   -1,
289   },
290 #if 0
291   {
292     MAIN_CONTROL_PREV_LEVEL,
293     &menu.main.button.prev_level,       IMG_MENU_BUTTON_PREV_LEVEL,
294     NULL,                               NULL,                   -1,
295     NULL,                               NULL,                   -1,
296   },
297   {
298     MAIN_CONTROL_NEXT_LEVEL,
299     &menu.main.button.next_level,       IMG_MENU_BUTTON_NEXT_LEVEL,
300     NULL,                               NULL,                   -1,
301     NULL,                               NULL,                   -1,
302   },
303 #endif
304   {
305     MAIN_CONTROL_CURRENT_LEVEL,
306     NULL,                               -1,
307     &menu.main.text.current_level,      main_text_current_level,FONT_VALUE_1,
308     NULL,                               NULL,                   -1,
309   },
310   {
311     MAIN_CONTROL_FIRST_LEVEL,
312     NULL,                               -1,
313     &menu.main.text.first_level,        main_text_first_level,  FONT_TEXT_3,
314     NULL,                               NULL,                   -1,
315   },
316   {
317     MAIN_CONTROL_LAST_LEVEL,
318     NULL,                               -1,
319     &menu.main.text.last_level,         main_text_last_level,   FONT_TEXT_3,
320     NULL,                               NULL,                   -1,
321   },
322   {
323     MAIN_CONTROL_LEVELSET_INFO,
324     NULL,                               -1,
325     &menu.main.text.level_info_1,       NULL,                   -1,
326     NULL,                               NULL,                   -1,
327   },
328   {
329     MAIN_CONTROL_LEVEL_INFO,
330     NULL,                               -1,
331     &menu.main.text.level_info_2,       NULL,                   -1,
332     NULL,                               NULL,                   -1,
333   },
334
335   {
336     -1,
337     NULL,                               -1,
338     NULL,                               NULL,                   -1,
339     NULL,                               NULL,                   -1,
340   }
341 };
342
343
344 static void InitializeMainControls()
345 {
346   boolean local_team_mode = (!options.network && setup.team_mode);
347   int i;
348
349   /* set main control text values to dynamically determined values */
350   sprintf(main_text_name,          "%s",   local_team_mode ? "Team:" : "Name:");
351   sprintf(main_text_current_level, "%s",   int2str(level_nr, 3));
352   sprintf(main_text_first_level,   "%03d", leveldir_current->first_level);
353   sprintf(main_text_last_level,    "%03d", leveldir_current->last_level);
354   sprintf(main_input_name,         "%s",   setup.player_name);
355
356   /* set main control screen positions to dynamically determined values */
357   for (i = 0; main_controls[i].nr != -1; i++)
358   {
359     int nr                  = main_controls[i].nr;
360     struct Rect *pos_button = main_controls[i].pos_button;
361     struct Rect *pos_text   = main_controls[i].pos_text;
362     struct Rect *pos_input  = main_controls[i].pos_input;
363     char *text              = main_controls[i].text;
364     char *input             = main_controls[i].input;
365     int button_graphic      = main_controls[i].button_graphic;
366     int font_text           = main_controls[i].font_text;
367     int font_input          = main_controls[i].font_input;
368
369     int font_text_width   = (font_text  != -1 ? getFontWidth(font_text)   : 0);
370     int font_text_height  = (font_text  != -1 ? getFontHeight(font_text)  : 0);
371     int font_input_width  = (font_input != -1 ? getFontWidth(font_input)  : 0);
372     int font_input_height = (font_input != -1 ? getFontHeight(font_input) : 0);
373     int text_chars  = (text  != NULL ? strlen(text)  : 0);
374     int input_chars = (input != NULL ? strlen(input) : 0);
375
376     int button_width =
377       (button_graphic != -1 ? graphic_info[button_graphic].width  : 0);
378     int button_height =
379       (button_graphic != -1 ? graphic_info[button_graphic].height : 0);
380     int text_width   = font_text_width * text_chars;
381     int text_height  = font_text_height;
382     int input_width  = font_input_width * input_chars;
383     int input_height = font_input_height;
384
385     if (nr == MAIN_CONTROL_NAME)
386     {
387 #if 0
388       if (menu.main.input.name.x == -1)
389         menu.main.input.name.x = menu.main.text.name.x + text_width;
390       if (menu.main.input.name.y == -1)
391         menu.main.input.name.y = menu.main.text.name.y;
392 #endif
393
394       menu.main.input.name.width  = font_input_width * MAX_PLAYER_NAME_LEN;
395       menu.main.input.name.height = font_input_height;
396     }
397
398     if (pos_button != NULL)
399     {
400       if (pos_button->width == 0)
401         pos_button->width = button_width;
402       if (pos_button->height == 0)
403         pos_button->height = button_height;
404     }
405
406     if (pos_text != NULL)
407     {
408       if (pos_text->x == -1 && pos_button != NULL)
409         pos_text->x = pos_button->x + pos_button->width;
410       if (pos_text->y == -1 && pos_button != NULL)
411         pos_text->y = pos_button->y;
412
413       if (pos_text->width == -1)
414         pos_text->width = text_width;
415       if (pos_text->height == -1)
416         pos_text->height = text_height;
417     }
418
419     if (pos_input != NULL)
420     {
421       if (pos_input->x == -1 && pos_text != NULL)
422         pos_input->x = pos_text->x + pos_text->width;
423       if (pos_input->y == -1 && pos_text != NULL)
424         pos_input->y = pos_text->y;
425
426       if (pos_input->width == -1)
427         pos_input->width = input_width;
428       if (pos_input->height == -1)
429         pos_input->height = input_height;
430     }
431   }
432 }
433
434 static void DrawCursorAndText_Main(int pos, boolean active)
435 {
436   int i;
437
438   for (i = 0; main_controls[i].nr != -1; i++)
439   {
440     if (main_controls[i].nr == pos)
441     {
442       struct Rect *pos_button = main_controls[i].pos_button;
443       struct Rect *pos_text   = main_controls[i].pos_text;
444       char *text              = main_controls[i].text;
445       int font_text           = main_controls[i].font_text;
446       int button_graphic      = main_controls[i].button_graphic;
447       int button_x = mSX + pos_button->x;
448       int button_y = mSY + pos_button->y;
449       int text_x   = mSX + pos_text->x;
450       int text_y   = mSY + pos_text->y;
451
452       if (active)
453       {
454         button_graphic = BUTTON_GRAPHIC_ACTIVE(button_graphic);
455         font_text = FONT_ACTIVE(font_text);
456       }
457
458       DrawBackground(button_x, button_y, pos_button->width, pos_button->height);
459       DrawGraphicThruMaskExt(drawto, button_x, button_y, button_graphic, 0);
460
461       DrawBackground(text_x, text_y, pos_text->width, pos_text->height);
462       DrawText(text_x, text_y, text, font_text);
463
464       break;
465     }
466   }
467 }
468
469 static boolean insideRect(struct Rect *rect, int x, int y)
470 {
471   return (rect != NULL &&
472           x >= rect->x && x < rect->x + rect->width &&
473           y >= rect->y && y < rect->y + rect->height);
474 }
475
476 static void drawCursorExt(int xpos, int ypos, boolean active, int graphic)
477 {
478   static int cursor_array[SCR_FIELDY];
479   int x = mSX + TILEX * xpos;
480   int y = mSY + TILEY * (MENU_SCREEN_START_YPOS + ypos);
481
482   if (xpos == 0)
483   {
484     if (graphic != -1)
485       cursor_array[ypos] = graphic;
486     else
487       graphic = cursor_array[ypos];
488   }
489
490   if (active)
491     graphic = BUTTON_GRAPHIC_ACTIVE(graphic);
492
493   DrawBackground(x, y, TILEX, TILEY);
494   DrawGraphicThruMaskExt(drawto, x, y, graphic, 0);
495 }
496
497 static void initCursor(int ypos, int graphic)
498 {
499   drawCursorExt(0, ypos, FALSE, graphic);
500 }
501
502 static void drawCursor(int ypos, boolean active)
503 {
504   drawCursorExt(0, ypos, active, -1);
505 }
506
507 static void drawCursorXY(int xpos, int ypos, int graphic)
508 {
509   drawCursorExt(xpos, ypos, FALSE, graphic);
510 }
511
512 static void drawChooseTreeCursor(int ypos, boolean active)
513 {
514   int last_game_status = game_status;   /* save current game status */
515
516   /* force LEVELS draw offset on artwork setup screen */
517   game_status = GAME_MODE_LEVELS;
518
519   drawCursorExt(0, ypos, active, 0);
520
521   game_status = last_game_status;       /* restore current game status */
522 }
523
524 void DrawHeadline()
525 {
526   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, PROGRAM_TITLE_STRING);
527   DrawTextSCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, PROGRAM_COPYRIGHT_STRING);
528 }
529
530 #if 0
531 static int getPrevlevelButtonPos()
532 {
533   return 10;
534 }
535
536 static int getCurrentLevelTextPos()
537 {
538   return (getPrevlevelButtonPos() + 1);
539 }
540
541 static int getNextLevelButtonPos()
542 {
543   return getPrevlevelButtonPos() + 3 + 1;
544 }
545
546 static int getLevelRangeTextPos()
547 {
548   return getNextLevelButtonPos() + 1;
549 }
550 #endif
551
552 void DrawTitleScreenImage(int nr)
553 {
554   int graphic = IMG_TITLESCREEN_1 + nr;
555   Bitmap *bitmap = graphic_info[graphic].bitmap;
556   int width  = graphic_info[graphic].src_image_width;
557   int height = graphic_info[graphic].src_image_height;
558   int src_x = 0, src_y = 0;
559   int dst_x, dst_y;
560
561   if (bitmap == NULL)
562     return;
563
564   if (width > WIN_XSIZE)
565   {
566     /* image width too large for window => center image horizontally */
567     src_x = (width - WIN_XSIZE) / 2;
568     width = WIN_XSIZE;
569   }
570
571   if (height > WIN_YSIZE)
572   {
573     /* image height too large for window => center image vertically */
574     src_y = (height - WIN_YSIZE) / 2;
575     height = WIN_YSIZE;
576   }
577
578   dst_x = (WIN_XSIZE - width) / 2;
579   dst_y = (WIN_YSIZE - height) / 2;
580
581   ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
582
583   if (DrawingOnBackground(dst_x, dst_y))
584     BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
585   else
586     BlitBitmap(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
587
588   redraw_mask = REDRAW_ALL;
589 }
590
591 void DrawTitleScreen()
592 {
593   KeyboardAutoRepeatOff();
594
595   SetMainBackgroundImage(IMG_BACKGROUND_TITLE);
596
597   HandleTitleScreen(0, 0, 0, 0, MB_MENU_INITIALIZE);
598
599   StopAnimation();
600 }
601
602 void DrawMainMenuExt(int redraw_mask, boolean do_fading)
603 {
604   static LevelDirTree *leveldir_last_valid = NULL;
605   boolean levelset_has_changed = FALSE;
606 #if 0
607   boolean local_team_mode = (!options.network && setup.team_mode);
608   char *name_text = (local_team_mode ? "Team:" : "Name:");
609   int name_width, level_width;
610 #endif
611   int i;
612
613   UnmapAllGadgets();
614   FadeSoundsAndMusic();
615
616   KeyboardAutoRepeatOn();
617   ActivateJoystick();
618
619   SetDrawDeactivationMask(REDRAW_NONE);
620   SetDrawBackgroundMask(REDRAW_FIELD);
621
622   audio.sound_deactivated = FALSE;
623
624   GetPlayerConfig();
625
626   /* needed if last screen was the playing screen, invoked from level editor */
627   if (level_editor_test_game)
628   {
629     game_status = GAME_MODE_EDITOR;
630     DrawLevelEd();
631
632     return;
633   }
634
635   /* needed if last screen was the editor screen */
636   UndrawSpecialEditorDoor();
637
638   /* needed if last screen was the setup screen and fullscreen state changed */
639   ToggleFullscreenIfNeeded();
640
641   /* leveldir_current may be invalid (level group, parent link) */
642   if (!validLevelSeries(leveldir_current))
643     leveldir_current = getFirstValidTreeInfoEntry(leveldir_last_valid);
644
645   if (leveldir_current != leveldir_last_valid)
646     levelset_has_changed = TRUE;
647
648   /* store valid level series information */
649   leveldir_last_valid = leveldir_current;
650
651   /* needed if last screen (level choice) changed graphics, sounds or music */
652   ReloadCustomArtwork(0);
653
654 #ifdef TARGET_SDL
655   SetDrawtoField(DRAW_BACKBUFFER);
656 #endif
657
658   if (setup.show_titlescreen &&
659       levelset_has_changed &&
660       graphic_info[IMG_TITLESCREEN_1].bitmap != NULL)
661   {
662     game_status = GAME_MODE_TITLE;
663
664     DrawTitleScreen();
665
666     return;
667   }
668
669   /* level_nr may have been set to value over handicap with level editor */
670   if (setup.handicap && level_nr > leveldir_current->handicap_level)
671     level_nr = leveldir_current->handicap_level;
672
673   LoadLevel(level_nr);
674
675   SetMainBackgroundImage(IMG_BACKGROUND_MAIN);
676   ClearWindow();
677
678   DrawHeadline();
679
680 #if 1
681   InitializeMainControls();
682
683   for (i = 0; main_controls[i].nr != -1; i++)
684   {
685     struct Rect *pos_button = main_controls[i].pos_button;
686     struct Rect *pos_text   = main_controls[i].pos_text;
687     struct Rect *pos_input  = main_controls[i].pos_input;
688     char *text              = main_controls[i].text;
689     char *input             = main_controls[i].input;
690     int button_graphic      = main_controls[i].button_graphic;
691     int font_text           = main_controls[i].font_text;
692     int font_input          = main_controls[i].font_input;
693
694     if (pos_button != NULL)
695       DrawGraphicThruMaskExt(drawto, mSX + pos_button->x, mSY + pos_button->y,
696                              button_graphic, 0);
697
698     if (pos_text != NULL && text != NULL)
699       DrawText(mSX + pos_text->x, mSY + pos_text->y, text, font_text);
700
701     if (pos_input != NULL && input != NULL)
702       DrawText(mSX + pos_input->x, mSY + pos_input->y, input, font_input);
703   }
704
705 #else
706
707   DrawText(mSX + 32, mSY + 2 * 32, name_text,       FONT_MENU_1);
708   DrawText(mSX + 32, mSY + 3 * 32, "Levelset",      FONT_MENU_1);
709   DrawText(mSX + 32, mSY + 4 * 32, "Hall Of Fame",  FONT_MENU_1);
710   DrawText(mSX + 32, mSY + 5 * 32, "Level Creator", FONT_MENU_1);
711   DrawText(mSX + 32, mSY + 6 * 32, "Info Screen",   FONT_MENU_1);
712   DrawText(mSX + 32, mSY + 7 * 32, "Start Game",    FONT_MENU_1);
713   DrawText(mSX + 32, mSY + 8 * 32, "Setup",         FONT_MENU_1);
714   DrawText(mSX + 32, mSY + 9 * 32, "Quit",          FONT_MENU_1);
715
716   /* calculated after (possible) reload of custom artwork */
717   name_width  = getTextWidth(name_text,  FONT_MENU_1);
718   level_width = 9 * 32;
719
720   DrawText(mSX + 32 + name_width, mSY + 2 * 32, setup.player_name,
721            FONT_INPUT_1);
722
723   DrawText(mSX + getCurrentLevelTextPos() * 32, mSY + 3 * 32,
724            int2str(level_nr, 3), FONT_VALUE_1);
725
726   {
727     int text_height = getFontHeight(FONT_TEXT_3);
728     int xpos = getLevelRangeTextPos() * 32 + 8;
729     int ypos2 = 3 * 32 + 16;
730     int ypos1 = ypos2 - text_height;
731
732     DrawTextF(mSX - SX + xpos, mSY - SY + ypos1, FONT_TEXT_3,
733               "%03d", leveldir_current->first_level);
734     DrawTextF(mSX - SX + xpos, mSY - SY + ypos2, FONT_TEXT_3,
735               "%03d", leveldir_current->last_level);
736   }
737
738   for (i = 0; i < 8; i++)
739     initCursor(i, (i == 1 || i == 4 || i == 6 ? IMG_MENU_BUTTON_ENTER_MENU :
740                    IMG_MENU_BUTTON));
741 #endif
742
743   DrawPreviewLevel(TRUE);
744
745   DrawTextSCentered(326, FONT_TITLE_2, "A Game by Artsoft Entertainment");
746
747   HandleMainMenu(0, 0, 0, 0, MB_MENU_INITIALIZE);
748
749   TapeStop();
750   if (TAPE_IS_EMPTY(tape))
751     LoadTape(level_nr);
752   DrawCompleteVideoDisplay();
753
754   PlayMenuSound();
755   PlayMenuMusic();
756
757   /* create gadgets for main menu screen */
758   FreeScreenGadgets();
759   CreateScreenGadgets();
760
761   /* map gadgets for main menu screen */
762   MapTapeButtons();
763   MapScreenMenuGadgets(SCREEN_MASK_MAIN);
764
765   DrawMaskedBorder(REDRAW_ALL);
766
767   if (do_fading)
768     FadeIn(redraw_mask);
769   else
770     BackToFront();
771
772   InitAnimation();
773
774   OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2);
775 }
776
777 void DrawAndFadeInMainMenu(int redraw_mask)
778 {
779   DrawMainMenuExt(redraw_mask, TRUE);
780 }
781
782 void DrawMainMenu()
783 {
784   DrawMainMenuExt(REDRAW_ALL, FALSE);
785 }
786
787 #if 0
788 static void gotoTopLevelDir()
789 {
790   /* move upwards to top level directory */
791   while (leveldir_current->node_parent)
792   {
793     /* write a "path" into level tree for easy navigation to last level */
794     if (leveldir_current->node_parent->node_group->cl_first == -1)
795     {
796       int num_leveldirs = numTreeInfoInGroup(leveldir_current);
797       int leveldir_pos = posTreeInfo(leveldir_current);
798       int num_page_entries;
799       int cl_first, cl_cursor;
800
801       if (num_leveldirs <= NUM_MENU_ENTRIES_ON_SCREEN)
802         num_page_entries = num_leveldirs;
803       else
804         num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
805
806       cl_first = MAX(0, leveldir_pos - num_page_entries + 1);
807       cl_cursor = leveldir_pos - cl_first;
808
809       leveldir_current->node_parent->node_group->cl_first = cl_first;
810       leveldir_current->node_parent->node_group->cl_cursor = cl_cursor;
811     }
812
813     leveldir_current = leveldir_current->node_parent;
814   }
815 }
816 #endif
817
818 void HandleTitleScreen(int mx, int my, int dx, int dy, int button)
819 {
820   static int title_nr = 0;
821   boolean return_to_main_menu = FALSE;
822   boolean use_fading_main_menu = TRUE;
823   boolean use_cross_fading = TRUE;
824
825   if (button == MB_MENU_INITIALIZE)
826   {
827     int last_game_status = game_status; /* save current game status */
828     title_nr = 0;
829
830     if (game_status == GAME_MODE_INFO)
831     {
832       if (graphic_info[IMG_TITLESCREEN_1].bitmap == NULL)
833       {
834         DrawInfoScreen_NotAvailable("Title screen information:",
835                                     "No title screen for this level set.");
836
837         return;
838       }
839
840       FadeSoundsAndMusic();
841
842       FadeOut(REDRAW_ALL);
843     }
844
845     /* force TITLE music on title info screen */
846     game_status = GAME_MODE_TITLE;
847
848     PlayMenuSound();
849     PlayMenuMusic();
850
851     game_status = last_game_status;     /* restore current game status */
852
853     DrawTitleScreenImage(title_nr);
854
855     FadeIn(REDRAW_ALL);
856
857     return;
858   }
859   else if (button == MB_MENU_LEAVE)
860   {
861     return_to_main_menu = TRUE;
862     use_fading_main_menu = FALSE;
863   }
864   else if (button == MB_MENU_CHOICE)
865   {
866     if (game_status == GAME_MODE_INFO &&
867         graphic_info[IMG_TITLESCREEN_1].bitmap == NULL)
868     {
869       info_mode = INFO_MODE_MAIN;
870       DrawInfoScreen();
871
872       return;
873     }
874
875     title_nr++;
876
877     if (!use_cross_fading)
878       FadeOut(REDRAW_ALL);
879
880     if (title_nr < MAX_NUM_TITLE_SCREENS &&
881         graphic_info[IMG_TITLESCREEN_1 + title_nr].bitmap != NULL)
882     {
883       if (use_cross_fading)
884         FadeCrossSaveBackbuffer();
885
886       DrawTitleScreenImage(title_nr);
887
888       if (use_cross_fading)
889         FadeCross(REDRAW_ALL);
890       else
891         FadeIn(REDRAW_ALL);
892     }
893     else
894     {
895       FadeSoundsAndMusic();
896
897       FadeOut(REDRAW_ALL);
898
899       return_to_main_menu = TRUE;
900     }
901   }
902
903   if (return_to_main_menu)
904   {
905     RedrawBackground();
906
907     if (game_status == GAME_MODE_INFO)
908     {
909       OpenDoor(DOOR_CLOSE_1 | DOOR_CLOSE_2 | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
910
911       info_mode = INFO_MODE_MAIN;
912       DrawInfoScreenExt(REDRAW_ALL, use_fading_main_menu);
913     }
914     else        /* default: return to main menu */
915     {
916       OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2 | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
917
918       game_status = GAME_MODE_MAIN;
919       DrawMainMenuExt(REDRAW_ALL, use_fading_main_menu);
920     }
921   }
922 }
923
924 void HandleMainMenu_SelectLevel(int step, int direction)
925 {
926   int old_level_nr = level_nr;
927   int new_level_nr;
928
929   new_level_nr = old_level_nr + step * direction;
930   if (new_level_nr < leveldir_current->first_level)
931     new_level_nr = leveldir_current->first_level;
932   if (new_level_nr > leveldir_current->last_level)
933     new_level_nr = leveldir_current->last_level;
934
935   if (setup.handicap && new_level_nr > leveldir_current->handicap_level)
936   {
937     /* skipping levels is only allowed when trying to skip single level */
938     if (setup.skip_levels && step == 1 &&
939         Request("Level still unsolved ! Skip despite handicap ?", REQ_ASK))
940     {
941       leveldir_current->handicap_level++;
942       SaveLevelSetup_SeriesInfo();
943     }
944
945     new_level_nr = leveldir_current->handicap_level;
946   }
947
948   if (new_level_nr != old_level_nr)
949   {
950     level_nr = new_level_nr;
951
952     DrawText(mSX + 11 * 32, mSY + 3 * 32, int2str(level_nr, 3), FONT_VALUE_1);
953
954     LoadLevel(level_nr);
955     DrawPreviewLevel(TRUE);
956
957     TapeErase();
958     LoadTape(level_nr);
959     DrawCompleteVideoDisplay();
960
961     /* needed because DrawPreviewLevel() takes some time */
962     BackToFront();
963     SyncDisplay();
964   }
965 }
966
967 #if 1
968
969 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
970 {
971   static int choice = MAIN_CONTROL_GAME;
972   int pos = choice;
973   int i;
974
975   if (button == MB_MENU_INITIALIZE)
976   {
977     DrawCursorAndText_Main(choice, TRUE);
978
979     return;
980   }
981
982   if (mx || my)         /* mouse input */
983   {
984     pos = -1;
985
986     for (i = 0; main_controls[i].nr != -1; i++)
987     {
988       if (insideRect(main_controls[i].pos_button, mx - mSX, my - mSY) ||
989           insideRect(main_controls[i].pos_text,   mx - mSX, my - mSY) ||
990           insideRect(main_controls[i].pos_input,  mx - mSX, my - mSY))
991       {
992         pos = main_controls[i].nr;
993
994         break;
995       }
996     }
997   }
998   else if (dx || dy)    /* keyboard input */
999   {
1000     if (dx > 0 && (choice == MAIN_CONTROL_INFO ||
1001                    choice == MAIN_CONTROL_SETUP))
1002       button = MB_MENU_CHOICE;
1003     else if (dy)
1004       pos = choice + dy;
1005   }
1006
1007   if (pos == MAIN_CONTROL_LEVELS && dx != 0 && button)
1008   {
1009     HandleMainMenu_SelectLevel(1, dx < 0 ? -1 : +1);
1010   }
1011   else if (pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT)
1012   {
1013     if (button)
1014     {
1015       if (pos != choice)
1016       {
1017         DrawCursorAndText_Main(choice, FALSE);
1018         DrawCursorAndText_Main(pos, TRUE);
1019
1020         choice = pos;
1021       }
1022     }
1023     else
1024     {
1025       if (pos == MAIN_CONTROL_NAME)
1026       {
1027         game_status = GAME_MODE_PSEUDO_TYPENAME;
1028
1029         HandleTypeName(strlen(setup.player_name), 0);
1030       }
1031       else if (pos == MAIN_CONTROL_LEVELS)
1032       {
1033         if (leveldir_first)
1034         {
1035           game_status = GAME_MODE_LEVELS;
1036
1037           SaveLevelSetup_LastSeries();
1038           SaveLevelSetup_SeriesInfo();
1039
1040 #if 0
1041           gotoTopLevelDir();
1042 #endif
1043
1044           DrawChooseLevel();
1045         }
1046       }
1047       else if (pos == MAIN_CONTROL_SCORES)
1048       {
1049         game_status = GAME_MODE_SCORES;
1050
1051         DrawHallOfFame(-1);
1052       }
1053       else if (pos == MAIN_CONTROL_EDITOR)
1054       {
1055         if (leveldir_current->readonly &&
1056             !strEqual(setup.player_name, "Artsoft"))
1057           Request("This level is read only !", REQ_CONFIRM);
1058
1059         game_status = GAME_MODE_EDITOR;
1060
1061         DrawLevelEd();
1062       }
1063       else if (pos == MAIN_CONTROL_INFO)
1064       {
1065         game_status = GAME_MODE_INFO;
1066         info_mode = INFO_MODE_MAIN;
1067
1068         DrawInfoScreen();
1069       }
1070       else if (pos == MAIN_CONTROL_GAME)
1071       {
1072         StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
1073       }
1074       else if (pos == MAIN_CONTROL_SETUP)
1075       {
1076         game_status = GAME_MODE_SETUP;
1077         setup_mode = SETUP_MODE_MAIN;
1078
1079         DrawSetupScreen();
1080       }
1081       else if (pos == MAIN_CONTROL_QUIT)
1082       {
1083         SaveLevelSetup_LastSeries();
1084         SaveLevelSetup_SeriesInfo();
1085
1086         if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
1087           game_status = GAME_MODE_QUIT;
1088       }
1089     }
1090   }
1091
1092   if (game_status == GAME_MODE_MAIN)
1093   {
1094     DrawPreviewLevel(FALSE);
1095     DoAnimation();
1096   }
1097 }
1098
1099 #else
1100
1101 void HandleMainMenu(int mx, int my, int dx, int dy, int button)
1102 {
1103   static int choice = 5;
1104   int x = 0;
1105   int y = choice;
1106
1107   if (button == MB_MENU_INITIALIZE)
1108   {
1109     drawCursor(choice, TRUE);
1110
1111     return;
1112   }
1113
1114   if (mx || my)         /* mouse input */
1115   {
1116     x = (mx - mSX) / 32;
1117     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
1118   }
1119   else if (dx || dy)    /* keyboard input */
1120   {
1121     if (dx && choice == 1)
1122       x = (dx < 0 ? 10 : 14);
1123     else if (dx > 0)
1124     {
1125       if (choice == 4 || choice == 6)
1126         button = MB_MENU_CHOICE;
1127     }
1128     else if (dy)
1129       y = choice + dy;
1130   }
1131
1132   if (y == 1 && dx != 0 && button)
1133   {
1134     HandleMainMenu_SelectLevel(1, dx < 0 ? -1 : +1);
1135   }
1136   else if (IN_VIS_FIELD(x, y) &&
1137            y >= 0 && y <= 7 && (y != 1 || x < 10))
1138   {
1139     if (button)
1140     {
1141       if (y != choice)
1142       {
1143         drawCursor(choice, FALSE);
1144         drawCursor(y, TRUE);
1145
1146         choice = y;
1147       }
1148     }
1149     else
1150     {
1151       if (y == 0)
1152       {
1153         game_status = GAME_MODE_PSEUDO_TYPENAME;
1154         HandleTypeName(strlen(setup.player_name), 0);
1155       }
1156       else if (y == 1)
1157       {
1158         if (leveldir_first)
1159         {
1160           game_status = GAME_MODE_LEVELS;
1161           SaveLevelSetup_LastSeries();
1162           SaveLevelSetup_SeriesInfo();
1163
1164 #if 0
1165           gotoTopLevelDir();
1166 #endif
1167
1168           DrawChooseLevel();
1169         }
1170       }
1171       else if (y == 2)
1172       {
1173         game_status = GAME_MODE_SCORES;
1174         DrawHallOfFame(-1);
1175       }
1176       else if (y == 3)
1177       {
1178         if (leveldir_current->readonly &&
1179             !strEqual(setup.player_name, "Artsoft"))
1180           Request("This level is read only !", REQ_CONFIRM);
1181         game_status = GAME_MODE_EDITOR;
1182         DrawLevelEd();
1183       }
1184       else if (y == 4)
1185       {
1186         game_status = GAME_MODE_INFO;
1187         info_mode = INFO_MODE_MAIN;
1188         DrawInfoScreen();
1189       }
1190       else if (y == 5)
1191       {
1192         StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
1193       }
1194       else if (y == 6)
1195       {
1196         game_status = GAME_MODE_SETUP;
1197         setup_mode = SETUP_MODE_MAIN;
1198
1199         DrawSetupScreen();
1200       }
1201       else if (y == 7)
1202       {
1203         SaveLevelSetup_LastSeries();
1204         SaveLevelSetup_SeriesInfo();
1205
1206         if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED))
1207           game_status = GAME_MODE_QUIT;
1208       }
1209     }
1210   }
1211
1212   if (game_status == GAME_MODE_MAIN)
1213   {
1214     DrawPreviewLevel(FALSE);
1215     DoAnimation();
1216   }
1217 }
1218
1219 #endif
1220
1221
1222 /* ========================================================================= */
1223 /* info screen functions                                                     */
1224 /* ========================================================================= */
1225
1226 static struct TokenInfo *info_info;
1227 static int num_info_info;
1228
1229 static void execInfoTitleScreen()
1230 {
1231   info_mode = INFO_MODE_TITLE;
1232   DrawInfoScreen();
1233 }
1234
1235 static void execInfoElements()
1236 {
1237   info_mode = INFO_MODE_ELEMENTS;
1238   DrawInfoScreen();
1239 }
1240
1241 static void execInfoMusic()
1242 {
1243   info_mode = INFO_MODE_MUSIC;
1244   DrawInfoScreen();
1245 }
1246
1247 static void execInfoCredits()
1248 {
1249   info_mode = INFO_MODE_CREDITS;
1250   DrawInfoScreen();
1251 }
1252
1253 static void execInfoProgram()
1254 {
1255   info_mode = INFO_MODE_PROGRAM;
1256   DrawInfoScreen();
1257 }
1258
1259 static void execInfoLevelSet()
1260 {
1261   info_mode = INFO_MODE_LEVELSET;
1262   DrawInfoScreen();
1263 }
1264
1265 static void execExitInfo()
1266 {
1267   game_status = GAME_MODE_MAIN;
1268   DrawMainMenu();
1269 }
1270
1271 static struct TokenInfo info_info_main[] =
1272 {
1273   { TYPE_ENTER_SCREEN,  execInfoTitleScreen,    "Title Screen"          },
1274   { TYPE_ENTER_SCREEN,  execInfoElements,       "Elements Info"         },
1275   { TYPE_ENTER_SCREEN,  execInfoMusic,          "Music Info"            },
1276   { TYPE_ENTER_SCREEN,  execInfoCredits,        "Credits"               },
1277   { TYPE_ENTER_SCREEN,  execInfoProgram,        "Program Info"          },
1278   { TYPE_ENTER_SCREEN,  execInfoLevelSet,       "Level Set Info"        },
1279   { TYPE_EMPTY,         NULL,                   ""                      },
1280   { TYPE_LEAVE_MENU,    execExitInfo,           "Exit"                  },
1281
1282   { 0,                  NULL,                   NULL                    }
1283 };
1284
1285 static void DrawCursorAndText_Info(int pos, boolean active)
1286 {
1287   int xpos = MENU_SCREEN_START_XPOS;
1288   int ypos = MENU_SCREEN_START_YPOS + pos;
1289   int font_nr = FONT_MENU_1;
1290
1291   if (active)
1292     font_nr = FONT_ACTIVE(font_nr);
1293
1294   DrawText(mSX + xpos * 32, mSY + ypos * 32, info_info[pos].text, font_nr);
1295
1296   if (info_info[pos].type & ~TYPE_SKIP_ENTRY)
1297     drawCursor(pos, active);
1298 }
1299
1300 static void DrawInfoScreen_Main(int redraw_mask, boolean do_fading)
1301 {
1302   int i;
1303
1304   UnmapAllGadgets();
1305   CloseDoor(DOOR_CLOSE_2);
1306
1307   ClearWindow();
1308
1309   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Info Screen");
1310
1311   info_info = info_info_main;
1312   num_info_info = 0;
1313
1314   for (i = 0; info_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
1315   {
1316 #if 0
1317     int xpos = MENU_SCREEN_START_XPOS;
1318     int ypos = MENU_SCREEN_START_YPOS + i;
1319     int font_nr = FONT_MENU_1;
1320 #endif
1321
1322     if (info_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
1323       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
1324     else if (info_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
1325       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
1326     else if (info_info[i].type & ~TYPE_SKIP_ENTRY)
1327       initCursor(i, IMG_MENU_BUTTON);
1328
1329 #if 1
1330     DrawCursorAndText_Info(i, FALSE);
1331 #else
1332     DrawText(mSX + xpos * 32, mSY + ypos * 32, info_info[i].text, font_nr);
1333 #endif
1334
1335     num_info_info++;
1336   }
1337
1338   HandleInfoScreen_Main(0, 0, 0, 0, MB_MENU_INITIALIZE);
1339
1340   PlayMenuSound();
1341   PlayMenuMusic();
1342
1343   DrawMaskedBorder(REDRAW_ALL);
1344
1345   if (do_fading)
1346     FadeIn(redraw_mask);
1347   else
1348     BackToFront();
1349
1350   InitAnimation();
1351 }
1352
1353 void HandleInfoScreen_Main(int mx, int my, int dx, int dy, int button)
1354 {
1355   static int choice_store[MAX_INFO_MODES];
1356   int choice = choice_store[info_mode];         /* always starts with 0 */
1357   int x = 0;
1358   int y = choice;
1359
1360   if (button == MB_MENU_INITIALIZE)
1361   {
1362     /* advance to first valid menu entry */
1363     while (choice < num_info_info &&
1364            info_info[choice].type & TYPE_SKIP_ENTRY)
1365       choice++;
1366     choice_store[info_mode] = choice;
1367
1368 #if 1
1369     DrawCursorAndText_Info(choice, TRUE);
1370 #else
1371     drawCursor(choice, TRUE);
1372 #endif
1373
1374     return;
1375   }
1376   else if (button == MB_MENU_LEAVE)
1377   {
1378     for (y = 0; y < num_info_info; y++)
1379     {
1380       if (info_info[y].type & TYPE_LEAVE_MENU)
1381       {
1382         void (*menu_callback_function)(void) = info_info[y].value;
1383
1384         menu_callback_function();
1385
1386         break;  /* absolutely needed because function changes 'info_info'! */
1387       }
1388     }
1389
1390     return;
1391   }
1392
1393   if (mx || my)         /* mouse input */
1394   {
1395     x = (mx - mSX) / 32;
1396     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
1397   }
1398   else if (dx || dy)    /* keyboard input */
1399   {
1400     if (dx)
1401     {
1402       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
1403
1404       if (info_info[choice].type & menu_navigation_type ||
1405           info_info[choice].type & TYPE_ENTER_SCREEN ||
1406           info_info[choice].type & TYPE_BOOLEAN_STYLE)
1407         button = MB_MENU_CHOICE;
1408     }
1409     else if (dy)
1410       y = choice + dy;
1411
1412     /* jump to next non-empty menu entry (up or down) */
1413     while (y > 0 && y < num_info_info - 1 &&
1414            info_info[y].type & TYPE_SKIP_ENTRY)
1415       y += dy;
1416   }
1417
1418   if (IN_VIS_FIELD(x, y) &&
1419       y >= 0 && y < num_info_info && info_info[y].type & ~TYPE_SKIP_ENTRY)
1420   {
1421     if (button)
1422     {
1423       if (y != choice)
1424       {
1425 #if 1
1426         DrawCursorAndText_Info(choice, FALSE);
1427         DrawCursorAndText_Info(y, TRUE);
1428 #else
1429         drawCursor(choice, FALSE);
1430         drawCursor(y, TRUE);
1431 #endif
1432
1433         choice = choice_store[info_mode] = y;
1434       }
1435     }
1436     else if (!(info_info[y].type & TYPE_GHOSTED))
1437     {
1438       if (info_info[y].type & TYPE_ENTER_OR_LEAVE)
1439       {
1440         void (*menu_callback_function)(void) = info_info[choice].value;
1441
1442         menu_callback_function();
1443       }
1444     }
1445   }
1446 }
1447
1448 void DrawInfoScreen_NotAvailable(char *text_title, char *text_error)
1449 {
1450   int ystart = 150;
1451   int ybottom = SYSIZE - 20;
1452
1453   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_LEVELSET);
1454
1455   ClearWindow();
1456   DrawHeadline();
1457
1458   DrawTextSCentered(100, FONT_TEXT_1, text_title);
1459
1460   DrawTextSCentered(ybottom, FONT_TEXT_4,
1461                     "Press any key or button for info menu");
1462
1463   DrawTextSCentered(ystart, FONT_TEXT_2, text_error);
1464 }
1465
1466 void DrawInfoScreen_HelpAnim(int start, int max_anims, boolean init)
1467 {
1468   static int infoscreen_step[MAX_INFO_ELEMENTS_ON_SCREEN];
1469   static int infoscreen_frame[MAX_INFO_ELEMENTS_ON_SCREEN];
1470   int xstart = mSX + 16;
1471   int ystart = mSY + 64 + 2 * 32;
1472   int ystep = TILEY + 4;
1473   int element, action, direction;
1474   int graphic;
1475   int delay;
1476   int sync_frame;
1477   int i, j;
1478
1479   if (init)
1480   {
1481     for (i = 0; i < MAX_INFO_ELEMENTS_ON_SCREEN; i++)
1482       infoscreen_step[i] = infoscreen_frame[i] = 0;
1483
1484     ClearWindow();
1485     DrawHeadline();
1486
1487     DrawTextSCentered(100, FONT_TEXT_1, "The Game Elements:");
1488
1489     DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
1490                       "Press any key or button for next page");
1491
1492     FrameCounter = 0;
1493   }
1494
1495   i = j = 0;
1496   while (helpanim_info[j].element != HELPANIM_LIST_END)
1497   {
1498     if (i >= start + MAX_INFO_ELEMENTS_ON_SCREEN ||
1499         i >= max_anims)
1500       break;
1501     else if (i < start)
1502     {
1503       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
1504         j++;
1505
1506       j++;
1507       i++;
1508
1509       continue;
1510     }
1511
1512     j += infoscreen_step[i - start];
1513
1514     element = helpanim_info[j].element;
1515     action = helpanim_info[j].action;
1516     direction = helpanim_info[j].direction;
1517
1518     if (element < 0)
1519       element = EL_UNKNOWN;
1520
1521     if (action != -1 && direction != -1)
1522       graphic = el_act_dir2img(element, action, direction);
1523     else if (action != -1)
1524       graphic = el_act2img(element, action);
1525     else if (direction != -1)
1526       graphic = el_dir2img(element, direction);
1527     else
1528       graphic = el2img(element);
1529
1530     delay = helpanim_info[j++].delay;
1531
1532     if (delay == -1)
1533       delay = 1000000;
1534
1535     if (infoscreen_frame[i - start] == 0)
1536     {
1537       sync_frame = 0;
1538       infoscreen_frame[i - start] = delay - 1;
1539     }
1540     else
1541     {
1542       sync_frame = delay - infoscreen_frame[i - start];
1543       infoscreen_frame[i - start]--;
1544     }
1545
1546     if (helpanim_info[j].element == HELPANIM_LIST_NEXT)
1547     {
1548       if (!infoscreen_frame[i - start])
1549         infoscreen_step[i - start] = 0;
1550     }
1551     else
1552     {
1553       if (!infoscreen_frame[i - start])
1554         infoscreen_step[i - start]++;
1555       while (helpanim_info[j].element != HELPANIM_LIST_NEXT)
1556         j++;
1557     }
1558
1559     j++;
1560
1561     ClearRectangleOnBackground(drawto, xstart, ystart + (i - start) * ystep,
1562                                TILEX, TILEY);
1563     DrawGraphicAnimationExt(drawto, xstart, ystart + (i - start) * ystep,
1564                             graphic, sync_frame, USE_MASKING);
1565
1566     if (init)
1567       DrawInfoScreen_HelpText(element, action, direction, i - start);
1568
1569     i++;
1570   }
1571
1572   redraw_mask |= REDRAW_FIELD;
1573
1574   FrameCounter++;
1575 }
1576
1577 static char *getHelpText(int element, int action, int direction)
1578 {
1579   char token[MAX_LINE_LEN];
1580
1581   strcpy(token, element_info[element].token_name);
1582
1583   if (action != -1)
1584     strcat(token, element_action_info[action].suffix);
1585
1586   if (direction != -1)
1587     strcat(token, element_direction_info[MV_DIR_TO_BIT(direction)].suffix);
1588
1589   return getHashEntry(helptext_info, token);
1590 }
1591
1592 void DrawInfoScreen_HelpText(int element, int action, int direction, int ypos)
1593 {
1594   int font_nr = FONT_LEVEL_NUMBER;
1595   int font_width = getFontWidth(font_nr);
1596   int sx = mSX + MINI_TILEX + TILEX + MINI_TILEX;
1597   int sy = mSY + 65 + 2 * 32 + 1;
1598   int ystep = TILEY + 4;
1599   int pad_x = sx - SX;
1600   int max_chars_per_line = (SXSIZE - pad_x - MINI_TILEX) / font_width;
1601   int max_lines_per_text = 2;    
1602   char *text = NULL;
1603
1604   if (action != -1 && direction != -1)          /* element.action.direction */
1605     text = getHelpText(element, action, direction);
1606
1607   if (text == NULL && action != -1)             /* element.action */
1608     text = getHelpText(element, action, -1);
1609
1610   if (text == NULL && direction != -1)          /* element.direction */
1611     text = getHelpText(element, -1, direction);
1612
1613   if (text == NULL)                             /* base element */
1614     text = getHelpText(element, -1, -1);
1615
1616   if (text == NULL)                             /* not found */
1617     text = "No description available";
1618
1619   if (strlen(text) <= max_chars_per_line)       /* only one line of text */
1620     sy += getFontHeight(font_nr) / 2;
1621
1622   DrawTextWrapped(sx, sy + ypos * ystep, text, font_nr,
1623                   max_chars_per_line, max_lines_per_text);
1624 }
1625
1626 void DrawInfoScreen_TitleScreen()
1627 {
1628   DrawTitleScreen();
1629 }
1630
1631 void HandleInfoScreen_TitleScreen(int button)
1632 {
1633   HandleTitleScreen(0, 0, 0, 0, button);
1634 }
1635
1636 void DrawInfoScreen_Elements()
1637 {
1638   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_ELEMENTS);
1639
1640   FadeOut(REDRAW_FIELD);
1641
1642   LoadHelpAnimInfo();
1643   LoadHelpTextInfo();
1644
1645   HandleInfoScreen_Elements(MB_MENU_INITIALIZE);
1646
1647   FadeIn(REDRAW_FIELD);
1648
1649   InitAnimation();
1650 }
1651
1652 void HandleInfoScreen_Elements(int button)
1653 {
1654   static unsigned long info_delay = 0;
1655   static int num_anims;
1656   static int num_pages;
1657   static int page;
1658   int anims_per_page = MAX_INFO_ELEMENTS_ON_SCREEN;
1659   int button_released = !button;
1660   int i;
1661
1662   if (button == MB_MENU_INITIALIZE)
1663   {
1664     boolean new_element = TRUE;
1665
1666     num_anims = 0;
1667     for (i = 0; helpanim_info[i].element != HELPANIM_LIST_END; i++)
1668     {
1669       if (helpanim_info[i].element == HELPANIM_LIST_NEXT)
1670         new_element = TRUE;
1671       else if (new_element)
1672       {
1673         num_anims++;
1674         new_element = FALSE;
1675       }
1676     }
1677
1678     num_pages = (num_anims + anims_per_page - 1) / anims_per_page;
1679     page = 0;
1680   }
1681   else if (button == MB_MENU_LEAVE)
1682   {
1683     info_mode = INFO_MODE_MAIN;
1684     DrawInfoScreen();
1685
1686     return;
1687   }
1688
1689   if (button_released || button == MB_MENU_INITIALIZE)
1690   {
1691     if (button != MB_MENU_INITIALIZE)
1692       page++;
1693
1694     if (page >= num_pages)
1695     {
1696       FadeSoundsAndMusic();
1697       FadeOut(REDRAW_FIELD);
1698
1699       info_mode = INFO_MODE_MAIN;
1700       DrawAndFadeInInfoScreen(REDRAW_FIELD);
1701
1702       return;
1703     }
1704
1705     if (button != MB_MENU_INITIALIZE)
1706       FadeCrossSaveBackbuffer();
1707
1708     DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, TRUE);
1709
1710     if (button != MB_MENU_INITIALIZE)
1711       FadeCross(REDRAW_FIELD);
1712   }
1713   else
1714   {
1715     if (DelayReached(&info_delay, GameFrameDelay))
1716       if (page < num_pages)
1717         DrawInfoScreen_HelpAnim(page * anims_per_page, num_anims, FALSE);
1718
1719     PlayMenuSoundIfLoop();
1720   }
1721 }
1722
1723 void DrawInfoScreen_Music()
1724 {
1725   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_MUSIC);
1726
1727   FadeOut(REDRAW_FIELD);
1728
1729   ClearWindow();
1730   DrawHeadline();
1731
1732   LoadMusicInfo();
1733
1734   HandleInfoScreen_Music(MB_MENU_INITIALIZE);
1735
1736   FadeIn(REDRAW_FIELD);
1737 }
1738
1739 void HandleInfoScreen_Music(int button)
1740 {
1741   static struct MusicFileInfo *list = NULL;
1742   int ystart = 150, dy = 30;
1743   int ybottom = SYSIZE - 20;
1744   int button_released = !button;
1745
1746   if (button == MB_MENU_INITIALIZE)
1747   {
1748     list = music_file_info;
1749
1750     if (list == NULL)
1751     {
1752       FadeSoundsAndMusic();
1753
1754       ClearWindow();
1755       DrawHeadline();
1756
1757       DrawTextSCentered(100, FONT_TEXT_1, "No music info for this level set.");
1758
1759       DrawTextSCentered(ybottom, FONT_TEXT_4,
1760                         "Press any key or button for info menu");
1761
1762       return;
1763     }
1764   }
1765   else if (button == MB_MENU_LEAVE)
1766   {
1767     info_mode = INFO_MODE_MAIN;
1768     DrawInfoScreen();
1769
1770     return;
1771   }
1772
1773   if (button_released || button == MB_MENU_INITIALIZE)
1774   {
1775     int y = 0;
1776
1777     if (button != MB_MENU_INITIALIZE)
1778       if (list != NULL)
1779         list = list->next;
1780
1781     if (list == NULL)
1782     {
1783       FadeSoundsAndMusic();
1784       FadeOut(REDRAW_FIELD);
1785
1786       info_mode = INFO_MODE_MAIN;
1787       DrawAndFadeInInfoScreen(REDRAW_FIELD);
1788
1789       return;
1790     }
1791
1792     FadeSoundsAndMusic();
1793
1794     if (button != MB_MENU_INITIALIZE)
1795       FadeCrossSaveBackbuffer();
1796
1797     ClearWindow();
1798     DrawHeadline();
1799
1800     if (list->is_sound)
1801     {
1802       int sound = list->music;
1803
1804       if (sound_info[sound].loop)
1805         PlaySoundLoop(sound);
1806       else
1807         PlaySound(sound);
1808
1809       DrawTextSCentered(100, FONT_TEXT_1, "The Game Background Sounds:");
1810     }
1811     else
1812     {
1813       PlayMusic(list->music);
1814
1815       DrawTextSCentered(100, FONT_TEXT_1, "The Game Background Music:");
1816     }
1817
1818     if (!strEqual(list->title, UNKNOWN_NAME))
1819     {
1820       if (!strEqual(list->title_header, UNKNOWN_NAME))
1821         DrawTextSCentered(ystart + y++ * dy, FONT_TEXT_2, list->title_header);
1822
1823       DrawTextFCentered(ystart + y++ * dy, FONT_TEXT_3, "\"%s\"", list->title);
1824     }
1825
1826     if (!strEqual(list->artist, UNKNOWN_NAME))
1827     {
1828       if (!strEqual(list->artist_header, UNKNOWN_NAME))
1829         DrawTextSCentered(ystart + y++ * dy, FONT_TEXT_2, list->artist_header);
1830       else
1831         DrawTextSCentered(ystart + y++ * dy, FONT_TEXT_2, "by");
1832
1833       DrawTextFCentered(ystart + y++ * dy, FONT_TEXT_3, "%s", list->artist);
1834     }
1835
1836     if (!strEqual(list->album, UNKNOWN_NAME))
1837     {
1838       if (!strEqual(list->album_header, UNKNOWN_NAME))
1839         DrawTextSCentered(ystart + y++ * dy, FONT_TEXT_2, list->album_header);
1840       else
1841         DrawTextSCentered(ystart + y++ * dy, FONT_TEXT_2, "from the album");
1842
1843       DrawTextFCentered(ystart + y++ * dy, FONT_TEXT_3, "\"%s\"", list->album);
1844     }
1845
1846     if (!strEqual(list->year, UNKNOWN_NAME))
1847     {
1848       if (!strEqual(list->year_header, UNKNOWN_NAME))
1849         DrawTextSCentered(ystart + y++ * dy, FONT_TEXT_2, list->year_header);
1850       else
1851         DrawTextSCentered(ystart + y++ * dy, FONT_TEXT_2, "from the year");
1852
1853       DrawTextFCentered(ystart + y++ * dy, FONT_TEXT_3, "%s", list->year);
1854     }
1855
1856     DrawTextSCentered(ybottom, FONT_TEXT_4,
1857                       "Press any key or button for next page");
1858
1859     if (button != MB_MENU_INITIALIZE)
1860       FadeCross(REDRAW_FIELD);
1861   }
1862
1863   if (list != NULL && list->is_sound && sound_info[list->music].loop)
1864     PlaySoundLoop(list->music);
1865 }
1866
1867 static boolean DrawInfoScreen_CreditsScreen(int screen_nr)
1868 {
1869   int ystart = 150, ystep = 30;
1870   int ybottom = SYSIZE - 20;
1871
1872   if (screen_nr > 8)
1873     return FALSE;
1874
1875   ClearWindow();
1876   DrawHeadline();
1877
1878   DrawTextSCentered(100, FONT_TEXT_1, "Credits:");
1879
1880   if (screen_nr == 0)
1881   {
1882     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
1883                       "Special thanks to");
1884     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_3,
1885                       "Peter Liepa");
1886     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_2,
1887                       "for creating");
1888     DrawTextSCentered(ystart + 3 * ystep, FONT_TEXT_3,
1889                       "\"Boulder Dash\"");
1890     DrawTextSCentered(ystart + 4 * ystep, FONT_TEXT_2,
1891                       "in the year");
1892     DrawTextSCentered(ystart + 5 * ystep, FONT_TEXT_3,
1893                       "1984");
1894     DrawTextSCentered(ystart + 6 * ystep, FONT_TEXT_2,
1895                       "published by");
1896     DrawTextSCentered(ystart + 7 * ystep, FONT_TEXT_3,
1897                       "First Star Software");
1898   }
1899   else if (screen_nr == 1)
1900   {
1901     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
1902                       "Special thanks to");
1903     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_3,
1904                       "Klaus Heinz & Volker Wertich");
1905     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_2,
1906                       "for creating");
1907     DrawTextSCentered(ystart + 3 * ystep, FONT_TEXT_3,
1908                       "\"Emerald Mine\"");
1909     DrawTextSCentered(ystart + 4 * ystep, FONT_TEXT_2,
1910                       "in the year");
1911     DrawTextSCentered(ystart + 5 * ystep, FONT_TEXT_3,
1912                       "1987");
1913     DrawTextSCentered(ystart + 6 * ystep, FONT_TEXT_2,
1914                       "published by");
1915     DrawTextSCentered(ystart + 7 * ystep, FONT_TEXT_3,
1916                       "Kingsoft");
1917   }
1918   else if (screen_nr == 2)
1919   {
1920     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
1921                       "Special thanks to");
1922     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_3,
1923                       "Michael Stopp & Philip Jespersen");
1924     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_2,
1925                       "for creating");
1926     DrawTextSCentered(ystart + 3 * ystep, FONT_TEXT_3,
1927                       "\"Supaplex\"");
1928     DrawTextSCentered(ystart + 4 * ystep, FONT_TEXT_2,
1929                       "in the year");
1930     DrawTextSCentered(ystart + 5 * ystep, FONT_TEXT_3,
1931                       "1991");
1932     DrawTextSCentered(ystart + 6 * ystep, FONT_TEXT_2,
1933                       "published by");
1934     DrawTextSCentered(ystart + 7 * ystep, FONT_TEXT_3,
1935                       "Digital Integration");
1936   }
1937   else if (screen_nr == 3)
1938   {
1939     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
1940                       "Special thanks to");
1941     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_3,
1942                       "Hiroyuki Imabayashi");
1943     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_2,
1944                       "for creating");
1945     DrawTextSCentered(ystart + 3 * ystep, FONT_TEXT_3,
1946                       "\"Sokoban\"");
1947     DrawTextSCentered(ystart + 4 * ystep, FONT_TEXT_2,
1948                       "in the year");
1949     DrawTextSCentered(ystart + 5 * ystep, FONT_TEXT_3,
1950                       "1982");
1951     DrawTextSCentered(ystart + 6 * ystep, FONT_TEXT_2,
1952                       "published by");
1953     DrawTextSCentered(ystart + 7 * ystep, FONT_TEXT_3,
1954                       "Thinking Rabbit");
1955   }
1956   else if (screen_nr == 4)
1957   {
1958     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
1959                       "Special thanks to");
1960     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_3,
1961                       "Alan Bond");
1962     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_2,
1963                       "and");
1964     DrawTextSCentered(ystart + 3 * ystep, FONT_TEXT_3,
1965                       "Jürgen Bonhagen");
1966     DrawTextSCentered(ystart + 4 * ystep, FONT_TEXT_2,
1967                       "for the continuous creation");
1968     DrawTextSCentered(ystart + 5 * ystep, FONT_TEXT_2,
1969                       "of outstanding level sets");
1970   }
1971   else if (screen_nr == 5)
1972   {
1973     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
1974                       "Thanks to");
1975     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_3,
1976                       "Peter Elzner");
1977     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_2,
1978                       "for ideas and inspiration by");
1979     DrawTextSCentered(ystart + 3 * ystep, FONT_TEXT_3,
1980                       "Diamond Caves");
1981
1982     DrawTextSCentered(ystart + 5 * ystep, FONT_TEXT_2,
1983                       "Thanks to");
1984     DrawTextSCentered(ystart + 6 * ystep, FONT_TEXT_3,
1985                       "Steffest");
1986     DrawTextSCentered(ystart + 7 * ystep, FONT_TEXT_2,
1987                       "for ideas and inspiration by");
1988     DrawTextSCentered(ystart + 8 * ystep, FONT_TEXT_3,
1989                       "DX-Boulderdash");
1990   }
1991   else if (screen_nr == 6)
1992   {
1993     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
1994                       "Thanks to");
1995     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_3,
1996                       "David Tritscher");
1997     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_2,
1998                       "for the new Emerald Mine engine");
1999   }
2000   else if (screen_nr == 7)
2001   {
2002     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
2003                       "Thanks to");
2004     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_3,
2005                       "Guido Schulz");
2006     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_2,
2007                       "for the initial DOS port");
2008
2009     DrawTextSCentered(ystart + 4 * ystep, FONT_TEXT_2,
2010                       "Thanks to");
2011     DrawTextSCentered(ystart + 5 * ystep, FONT_TEXT_3,
2012                       "Karl Hörnell");
2013     DrawTextSCentered(ystart + 6 * ystep, FONT_TEXT_2,
2014                       "for some additional toons");
2015   }
2016   else if (screen_nr == 8)
2017   {
2018     DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
2019                       "And not to forget:");
2020     DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_2,
2021                       "Many thanks to");
2022     DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_3,
2023                       "All those who contributed");
2024     DrawTextSCentered(ystart + 3 * ystep, FONT_TEXT_3,
2025                       "levels to this game");
2026     DrawTextSCentered(ystart + 4 * ystep, FONT_TEXT_3,
2027                       "since 1995");
2028   }
2029 #if 0
2030   else
2031   {
2032     return FALSE;
2033   }
2034 #endif
2035
2036   DrawTextSCentered(ybottom, FONT_TEXT_4,
2037                     "Press any key or button for next page");
2038
2039   return TRUE;
2040 }
2041
2042 void DrawInfoScreen_Credits()
2043 {
2044   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_CREDITS);
2045
2046   FadeSoundsAndMusic();
2047
2048   FadeOut(REDRAW_FIELD);
2049
2050   HandleInfoScreen_Credits(MB_MENU_INITIALIZE);
2051
2052   FadeIn(REDRAW_FIELD);
2053 }
2054
2055 void HandleInfoScreen_Credits(int button)
2056 {
2057   static int screen_nr = 0;
2058
2059   if (button == MB_MENU_INITIALIZE)
2060   {
2061     screen_nr = 0;
2062
2063     DrawInfoScreen_CreditsScreen(screen_nr);
2064   }
2065   else if (button == MB_MENU_LEAVE)
2066   {
2067     info_mode = INFO_MODE_MAIN;
2068     DrawInfoScreen();
2069
2070     return;
2071   }
2072   else if (button == MB_MENU_CHOICE)
2073   {
2074     boolean show_screen;
2075
2076     screen_nr++;
2077
2078     FadeCrossSaveBackbuffer();
2079
2080     show_screen = DrawInfoScreen_CreditsScreen(screen_nr);
2081   
2082     if (show_screen)
2083     {
2084       FadeCross(REDRAW_FIELD);
2085     }
2086     else
2087     {
2088       FadeSoundsAndMusic();
2089       FadeOut(REDRAW_FIELD);
2090
2091       info_mode = INFO_MODE_MAIN;
2092       DrawAndFadeInInfoScreen(REDRAW_FIELD);
2093     }
2094   }
2095   else
2096   {
2097     PlayMenuSoundIfLoop();
2098   }
2099 }
2100
2101 void DrawInfoScreen_Program()
2102 {
2103   int ystart = 150, ystep = 30;
2104   int ybottom = SYSIZE - 20;
2105
2106   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_PROGRAM);
2107
2108   FadeOut(REDRAW_FIELD);
2109
2110   ClearWindow();
2111   DrawHeadline();
2112
2113   DrawTextSCentered(100, FONT_TEXT_1, "Program Information:");
2114
2115   DrawTextSCentered(ystart + 0 * ystep, FONT_TEXT_2,
2116                     "This game is Freeware!");
2117   DrawTextSCentered(ystart + 1 * ystep, FONT_TEXT_2,
2118                     "If you like it, send e-mail to:");
2119   DrawTextSCentered(ystart + 2 * ystep, FONT_TEXT_3,
2120                     PROGRAM_EMAIL_STRING);
2121   DrawTextSCentered(ystart + 3 * ystep, FONT_TEXT_2,
2122                     "or SnailMail to:");
2123   DrawTextSCentered(ystart + 4 * ystep + 0, FONT_TEXT_3,
2124                     "Holger Schemel");
2125   DrawTextSCentered(ystart + 4 * ystep + 20, FONT_TEXT_3,
2126                     "Detmolder Strasse 189");
2127   DrawTextSCentered(ystart + 4 * ystep + 40, FONT_TEXT_3,
2128                     "33604 Bielefeld");
2129   DrawTextSCentered(ystart + 4 * ystep + 60, FONT_TEXT_3,
2130                     "Germany");
2131   DrawTextSCentered(ystart + 7 * ystep, FONT_TEXT_2,
2132                     "More information and levels:");
2133   DrawTextSCentered(ystart + 8 * ystep, FONT_TEXT_3,
2134                     PROGRAM_WEBSITE_STRING);
2135   DrawTextSCentered(ystart + 9 * ystep, FONT_TEXT_2,
2136                     "If you have created new levels,");
2137   DrawTextSCentered(ystart + 10 * ystep, FONT_TEXT_2,
2138                     "send them to me to include them!");
2139   DrawTextSCentered(ystart + 11 * ystep, FONT_TEXT_2,
2140                     ":-)");
2141
2142   DrawTextSCentered(ybottom, FONT_TEXT_4,
2143                     "Press any key or button for info menu");
2144
2145   FadeIn(REDRAW_FIELD);
2146 }
2147
2148 void HandleInfoScreen_Program(int button)
2149 {
2150   int button_released = !button;
2151
2152   if (button == MB_MENU_LEAVE)
2153   {
2154     info_mode = INFO_MODE_MAIN;
2155     DrawInfoScreen();
2156
2157     return;
2158   }
2159
2160   if (button_released)
2161   {
2162     FadeSoundsAndMusic();
2163     FadeOut(REDRAW_FIELD);
2164
2165     info_mode = INFO_MODE_MAIN;
2166     DrawAndFadeInInfoScreen(REDRAW_FIELD);
2167   }
2168   else
2169   {
2170     PlayMenuSoundIfLoop();
2171   }
2172 }
2173
2174 void DrawInfoScreen_LevelSet()
2175 {
2176   int ystart = 150;
2177   int ybottom = SYSIZE - 20;
2178   char *filename = getLevelSetInfoFilename();
2179   int font_nr = FONT_LEVEL_NUMBER;
2180   int font_width = getFontWidth(font_nr);
2181   int font_height = getFontHeight(font_nr);
2182   int pad_x = 32;
2183   int pad_y = ystart;
2184   int sx = SX + pad_x;
2185   int sy = SY + pad_y;
2186   int max_chars_per_line = (SXSIZE - 2 * pad_x) / font_width;
2187   int max_lines_per_screen = (SYSIZE - pad_y) / font_height - 1;
2188
2189   SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_LEVELSET);
2190
2191   FadeOut(REDRAW_FIELD);
2192
2193   ClearWindow();
2194   DrawHeadline();
2195
2196   DrawTextSCentered(100, FONT_TEXT_1, "Level Set Information:");
2197
2198   DrawTextSCentered(ybottom, FONT_TEXT_4,
2199                     "Press any key or button for info menu");
2200
2201   if (filename != NULL)
2202     DrawTextFromFile(sx, sy, filename, font_nr, max_chars_per_line,
2203                      max_lines_per_screen);
2204   else
2205     DrawTextSCentered(ystart, FONT_TEXT_2,
2206                       "No information for this level set.");
2207
2208   FadeIn(REDRAW_FIELD);
2209 }
2210
2211 void HandleInfoScreen_LevelSet(int button)
2212 {
2213   int button_released = !button;
2214
2215   if (button == MB_MENU_LEAVE)
2216   {
2217     info_mode = INFO_MODE_MAIN;
2218     DrawInfoScreen();
2219
2220     return;
2221   }
2222
2223   if (button_released)
2224   {
2225     FadeSoundsAndMusic();
2226     FadeOut(REDRAW_FIELD);
2227
2228     info_mode = INFO_MODE_MAIN;
2229     DrawAndFadeInInfoScreen(REDRAW_FIELD);
2230   }
2231   else
2232   {
2233     PlayMenuSoundIfLoop();
2234   }
2235 }
2236
2237 static void DrawInfoScreenExt(int redraw_mask, boolean do_fading)
2238 {
2239   SetMainBackgroundImage(IMG_BACKGROUND_INFO);
2240
2241   if (info_mode == INFO_MODE_TITLE)
2242     DrawInfoScreen_TitleScreen();
2243   else if (info_mode == INFO_MODE_ELEMENTS)
2244     DrawInfoScreen_Elements();
2245   else if (info_mode == INFO_MODE_MUSIC)
2246     DrawInfoScreen_Music();
2247   else if (info_mode == INFO_MODE_CREDITS)
2248     DrawInfoScreen_Credits();
2249   else if (info_mode == INFO_MODE_PROGRAM)
2250     DrawInfoScreen_Program();
2251   else if (info_mode == INFO_MODE_LEVELSET)
2252     DrawInfoScreen_LevelSet();
2253   else
2254     DrawInfoScreen_Main(redraw_mask, do_fading);
2255
2256   if (info_mode != INFO_MODE_MAIN &&
2257       info_mode != INFO_MODE_TITLE &&
2258       info_mode != INFO_MODE_MUSIC)
2259   {
2260     PlayMenuSound();
2261     PlayMenuMusic();
2262   }
2263 }
2264
2265 void DrawAndFadeInInfoScreen(int redraw_mask)
2266 {
2267   DrawInfoScreenExt(redraw_mask, TRUE);
2268 }
2269
2270 void DrawInfoScreen()
2271 {
2272   DrawInfoScreenExt(REDRAW_ALL, FALSE);
2273 }
2274
2275 void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
2276 {
2277   if (info_mode == INFO_MODE_TITLE)
2278     HandleInfoScreen_TitleScreen(button);
2279   else if (info_mode == INFO_MODE_ELEMENTS)
2280     HandleInfoScreen_Elements(button);
2281   else if (info_mode == INFO_MODE_MUSIC)
2282     HandleInfoScreen_Music(button);
2283   else if (info_mode == INFO_MODE_CREDITS)
2284     HandleInfoScreen_Credits(button);
2285   else if (info_mode == INFO_MODE_PROGRAM)
2286     HandleInfoScreen_Program(button);
2287   else if (info_mode == INFO_MODE_LEVELSET)
2288     HandleInfoScreen_LevelSet(button);
2289   else
2290     HandleInfoScreen_Main(mx, my, dx, dy, button);
2291
2292   DoAnimation();
2293 }
2294
2295
2296 /* ========================================================================= */
2297 /* type name functions                                                       */
2298 /* ========================================================================= */
2299
2300 void HandleTypeName(int newxpos, Key key)
2301 {
2302   static int xpos = 0, ypos = 2;
2303   int font_width = getFontWidth(FONT_INPUT_1_ACTIVE);
2304   int name_width = getFontWidth(FONT_MENU_1) * strlen("Name:");
2305   int startx = mSX + 32 + name_width;
2306   int starty = mSY + ypos * 32;
2307
2308   if (newxpos)
2309   {
2310     xpos = newxpos;
2311
2312     DrawText(startx, starty, setup.player_name, FONT_INPUT_1_ACTIVE);
2313     DrawText(startx + xpos * font_width, starty, "_", FONT_INPUT_1_ACTIVE);
2314
2315     return;
2316   }
2317
2318   if (((key >= KSYM_A && key <= KSYM_Z) ||
2319        (key >= KSYM_a && key <= KSYM_z)) && 
2320       xpos < MAX_PLAYER_NAME_LEN)
2321   {
2322     char ascii;
2323
2324     if (key >= KSYM_A && key <= KSYM_Z)
2325       ascii = 'A' + (char)(key - KSYM_A);
2326     else
2327       ascii = 'a' + (char)(key - KSYM_a);
2328
2329     setup.player_name[xpos] = ascii;
2330     setup.player_name[xpos + 1] = 0;
2331     xpos++;
2332
2333     DrawText(startx, starty, setup.player_name, FONT_INPUT_1_ACTIVE);
2334     DrawText(startx + xpos * font_width, starty, "_", FONT_INPUT_1_ACTIVE);
2335   }
2336   else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0)
2337   {
2338     xpos--;
2339     setup.player_name[xpos] = 0;
2340
2341     DrawText(startx + xpos * font_width, starty, "_ ", FONT_INPUT_1_ACTIVE);
2342   }
2343   else if (key == KSYM_Return && xpos > 0)
2344   {
2345     DrawText(startx, starty, setup.player_name, FONT_INPUT_1);
2346     DrawText(startx + xpos * font_width, starty, " ", FONT_INPUT_1_ACTIVE);
2347
2348     SaveSetup();
2349     game_status = GAME_MODE_MAIN;
2350   }
2351 }
2352
2353
2354 /* ========================================================================= */
2355 /* tree menu functions                                                       */
2356 /* ========================================================================= */
2357
2358 static void DrawChooseTree(TreeInfo **ti_ptr)
2359 {
2360   UnmapAllGadgets();
2361
2362   FreeScreenGadgets();
2363   CreateScreenGadgets();
2364
2365   CloseDoor(DOOR_CLOSE_2);
2366
2367   ClearWindow();
2368
2369   HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr);
2370   MapScreenTreeGadgets(*ti_ptr);
2371
2372   FadeToFront();
2373   InitAnimation();
2374 }
2375
2376 static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti)
2377 {
2378   struct GadgetInfo *gi = screen_gadget[id];
2379   int items_max, items_visible, item_position;
2380
2381   items_max = numTreeInfoInGroup(ti);
2382   items_visible = NUM_MENU_ENTRIES_ON_SCREEN;
2383   item_position = first_entry;
2384
2385   if (item_position > items_max - items_visible)
2386     item_position = items_max - items_visible;
2387
2388   ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
2389                GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
2390                GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
2391 }
2392
2393 static void drawChooseTreeList(int first_entry, int num_page_entries,
2394                                TreeInfo *ti)
2395 {
2396   int i;
2397   char *title_string = NULL;
2398   int yoffset_sets = MENU_TITLE1_YPOS;
2399   int yoffset_setup = 16;
2400   int yoffset = (ti->type == TREE_TYPE_LEVEL_DIR ? yoffset_sets :
2401                  yoffset_setup);
2402   int last_game_status = game_status;   /* save current game status */
2403
2404   title_string = ti->infotext;
2405
2406   DrawTextSCentered(mSY - SY + yoffset, FONT_TITLE_1, title_string);
2407
2408   /* force LEVELS font on artwork setup screen */
2409   game_status = GAME_MODE_LEVELS;
2410
2411   /* clear tree list area, but not title or scrollbar */
2412   DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32,
2413                  SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset,
2414                  MAX_MENU_ENTRIES_ON_SCREEN * 32);
2415
2416   for (i = 0; i < num_page_entries; i++)
2417   {
2418     TreeInfo *node, *node_first;
2419     int entry_pos = first_entry + i;
2420     int xpos = MENU_SCREEN_START_XPOS;
2421     int ypos = MENU_SCREEN_START_YPOS + i;
2422     int startx = mSX + xpos * 32;
2423     int starty = mSY + ypos * 32;
2424     int font_nr = FONT_TEXT_1;
2425     int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
2426     int startx_text = startx + font_xoffset;
2427     int startx_scrollbar = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset;
2428     int text_size = startx_scrollbar - startx_text;
2429     int max_buffer_len = text_size / getFontWidth(font_nr);
2430     char buffer[max_buffer_len + 1];
2431
2432     node_first = getTreeInfoFirstGroupEntry(ti);
2433     node = getTreeInfoFromPos(node_first, entry_pos);
2434
2435     strncpy(buffer, node->name, max_buffer_len);
2436     buffer[max_buffer_len] = '\0';
2437
2438     DrawText(startx, starty, buffer, font_nr + node->color);
2439
2440     if (node->parent_link)
2441       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
2442     else if (node->level_group)
2443       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
2444     else
2445       initCursor(i, IMG_MENU_BUTTON);
2446   }
2447
2448   game_status = last_game_status;       /* restore current game status */
2449
2450   redraw_mask |= REDRAW_FIELD;
2451 }
2452
2453 static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti)
2454 {
2455   TreeInfo *node, *node_first;
2456   int x, last_redraw_mask = redraw_mask;
2457   int ypos = MENU_TITLE2_YPOS;
2458
2459   if (ti->type != TREE_TYPE_LEVEL_DIR)
2460     return;
2461
2462   node_first = getTreeInfoFirstGroupEntry(ti);
2463   node = getTreeInfoFromPos(node_first, entry_pos);
2464
2465   DrawBackground(SX, SY + ypos, SXSIZE, getFontHeight(FONT_TITLE_2));
2466
2467   if (node->parent_link)
2468     DrawTextFCentered(ypos, FONT_TITLE_2, "leave group \"%s\"",
2469                       node->class_desc);
2470   else if (node->level_group)
2471     DrawTextFCentered(ypos, FONT_TITLE_2, "enter group \"%s\"",
2472                       node->class_desc);
2473   else if (ti->type == TREE_TYPE_LEVEL_DIR)
2474     DrawTextFCentered(ypos, FONT_TITLE_2, "%3d levels (%s)",
2475                       node->levels, node->class_desc);
2476
2477   /* let BackToFront() redraw only what is needed */
2478   redraw_mask = last_redraw_mask | REDRAW_TILES;
2479   for (x = 0; x < SCR_FIELDX; x++)
2480     MarkTileDirty(x, 1);
2481 }
2482
2483 static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
2484                              TreeInfo **ti_ptr)
2485 {
2486   TreeInfo *ti = *ti_ptr;
2487   int x = 0;
2488   int y = ti->cl_cursor;
2489   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2490   int num_entries = numTreeInfoInGroup(ti);
2491   int num_page_entries;
2492   int last_game_status = game_status;   /* save current game status */
2493   boolean position_set_by_scrollbar = (dx == 999);
2494
2495   /* force LEVELS draw offset on choose level and artwork setup screen */
2496   game_status = GAME_MODE_LEVELS;
2497
2498   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
2499     num_page_entries = num_entries;
2500   else
2501     num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
2502
2503   game_status = last_game_status;       /* restore current game status */
2504
2505   if (button == MB_MENU_INITIALIZE)
2506   {
2507     int num_entries = numTreeInfoInGroup(ti);
2508     int entry_pos = posTreeInfo(ti);
2509
2510     if (ti->cl_first == -1)
2511     {
2512       /* only on initialization */
2513       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
2514       ti->cl_cursor = entry_pos - ti->cl_first;
2515     }
2516     else if (ti->cl_cursor >= num_page_entries ||
2517              (num_entries > num_page_entries &&
2518               num_entries - ti->cl_first < num_page_entries))
2519     {
2520       /* only after change of list size (by custom graphic configuration) */
2521       ti->cl_first = MAX(0, entry_pos - num_page_entries + 1);
2522       ti->cl_cursor = entry_pos - ti->cl_first;
2523     }
2524
2525     if (position_set_by_scrollbar)
2526       ti->cl_first = dy;
2527     else
2528       AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2529                                 ti->cl_first, ti);
2530
2531     drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2532     drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2533     drawChooseTreeCursor(ti->cl_cursor, FC_RED);
2534
2535     return;
2536   }
2537   else if (button == MB_MENU_LEAVE)
2538   {
2539     if (ti->node_parent)
2540     {
2541       *ti_ptr = ti->node_parent;
2542       DrawChooseTree(ti_ptr);
2543     }
2544     else if (game_status == GAME_MODE_SETUP)
2545     {
2546       if (game_status == GAME_MODE_SETUP)
2547       {
2548         if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
2549           execSetupGraphics();
2550         else
2551           execSetupArtwork();
2552       }
2553     }
2554     else
2555     {
2556       game_status = GAME_MODE_MAIN;
2557       DrawMainMenu();
2558     }
2559
2560     return;
2561   }
2562
2563   if (mx || my)         /* mouse input */
2564   {
2565     int last_game_status = game_status; /* save current game status */
2566
2567     /* force LEVELS draw offset on artwork setup screen */
2568     game_status = GAME_MODE_LEVELS;
2569
2570     x = (mx - mSX) / 32;
2571     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
2572
2573     game_status = last_game_status;     /* restore current game status */
2574   }
2575   else if (dx || dy)    /* keyboard or scrollbar/scrollbutton input */
2576   {
2577     /* move cursor instead of scrolling when already at start/end of list */
2578     if (dy == -1 * SCROLL_LINE && ti->cl_first == 0)
2579       dy = -1;
2580     else if (dy == +1 * SCROLL_LINE &&
2581              ti->cl_first + num_page_entries == num_entries)
2582       dy = 1;
2583
2584     /* handle scrolling screen one line or page */
2585     if (ti->cl_cursor + dy < 0 ||
2586         ti->cl_cursor + dy > num_page_entries - 1)
2587     {
2588       if (ABS(dy) == SCROLL_PAGE)
2589         step = num_page_entries - 1;
2590
2591       if (dy < 0 && ti->cl_first > 0)
2592       {
2593         /* scroll page/line up */
2594
2595         ti->cl_first -= step;
2596         if (ti->cl_first < 0)
2597           ti->cl_first = 0;
2598
2599         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2600         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2601         drawChooseTreeCursor(ti->cl_cursor, FC_RED);
2602         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2603                                   ti->cl_first, ti);
2604       }
2605       else if (dy > 0 && ti->cl_first + num_page_entries < num_entries)
2606       {
2607         /* scroll page/line down */
2608
2609         ti->cl_first += step;
2610         if (ti->cl_first + num_page_entries > num_entries)
2611           ti->cl_first = MAX(0, num_entries - num_page_entries);
2612
2613         drawChooseTreeList(ti->cl_first, num_page_entries, ti);
2614         drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti);
2615         drawChooseTreeCursor(ti->cl_cursor, FC_RED);
2616         AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL,
2617                                   ti->cl_first, ti);
2618       }
2619
2620       return;
2621     }
2622
2623     /* handle moving cursor one line */
2624     y = ti->cl_cursor + dy;
2625   }
2626
2627   if (dx == 1)
2628   {
2629     TreeInfo *node_first, *node_cursor;
2630     int entry_pos = ti->cl_first + y;
2631
2632     node_first = getTreeInfoFirstGroupEntry(ti);
2633     node_cursor = getTreeInfoFromPos(node_first, entry_pos);
2634
2635     if (node_cursor->node_group)
2636     {
2637       node_cursor->cl_first = ti->cl_first;
2638       node_cursor->cl_cursor = ti->cl_cursor;
2639       *ti_ptr = node_cursor->node_group;
2640       DrawChooseTree(ti_ptr);
2641
2642       return;
2643     }
2644   }
2645   else if (dx == -1 && ti->node_parent)
2646   {
2647     *ti_ptr = ti->node_parent;
2648     DrawChooseTree(ti_ptr);
2649
2650     return;
2651   }
2652
2653   if (!anyScrollbarGadgetActive() &&
2654       IN_VIS_FIELD(x, y) &&
2655       mx < screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x &&
2656       y >= 0 && y < num_page_entries)
2657   {
2658     if (button)
2659     {
2660       if (y != ti->cl_cursor)
2661       {
2662         drawChooseTreeCursor(y, FC_RED);
2663         drawChooseTreeCursor(ti->cl_cursor, FC_BLUE);
2664         drawChooseTreeInfo(ti->cl_first + y, ti);
2665         ti->cl_cursor = y;
2666       }
2667     }
2668     else
2669     {
2670       TreeInfo *node_first, *node_cursor;
2671       int entry_pos = ti->cl_first + y;
2672
2673       node_first = getTreeInfoFirstGroupEntry(ti);
2674       node_cursor = getTreeInfoFromPos(node_first, entry_pos);
2675
2676       if (node_cursor->node_group)
2677       {
2678         node_cursor->cl_first = ti->cl_first;
2679         node_cursor->cl_cursor = ti->cl_cursor;
2680         *ti_ptr = node_cursor->node_group;
2681         DrawChooseTree(ti_ptr);
2682       }
2683       else if (node_cursor->parent_link)
2684       {
2685         *ti_ptr = node_cursor->node_parent;
2686         DrawChooseTree(ti_ptr);
2687       }
2688       else
2689       {
2690         node_cursor->cl_first = ti->cl_first;
2691         node_cursor->cl_cursor = ti->cl_cursor;
2692         *ti_ptr = node_cursor;
2693
2694         if (ti->type == TREE_TYPE_LEVEL_DIR)
2695         {
2696           LoadLevelSetup_SeriesInfo();
2697
2698           SaveLevelSetup_LastSeries();
2699           SaveLevelSetup_SeriesInfo();
2700           TapeErase();
2701         }
2702
2703         if (game_status == GAME_MODE_SETUP)
2704         {
2705           if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
2706             execSetupGraphics();
2707           else
2708             execSetupArtwork();
2709         }
2710         else
2711         {
2712           game_status = GAME_MODE_MAIN;
2713           DrawMainMenu();
2714         }
2715       }
2716     }
2717   }
2718 }
2719
2720 void DrawChooseLevel()
2721 {
2722   SetMainBackgroundImage(IMG_BACKGROUND_LEVELS);
2723
2724   DrawChooseTree(&leveldir_current);
2725
2726   PlayMenuSound();
2727   PlayMenuMusic();
2728 }
2729
2730 void HandleChooseLevel(int mx, int my, int dx, int dy, int button)
2731 {
2732   HandleChooseTree(mx, my, dx, dy, button, &leveldir_current);
2733
2734   DoAnimation();
2735 }
2736
2737 void DrawHallOfFame(int highlight_position)
2738 {
2739   UnmapAllGadgets();
2740   FadeSoundsAndMusic();
2741   CloseDoor(DOOR_CLOSE_2);
2742
2743   if (highlight_position < 0) 
2744     LoadScore(level_nr);
2745
2746   FadeOut(REDRAW_FIELD);
2747
2748   InitAnimation();
2749
2750   PlayMenuSound();
2751   PlayMenuMusic();
2752
2753   HandleHallOfFame(highlight_position, 0, 0, 0, MB_MENU_INITIALIZE);
2754
2755   FadeIn(REDRAW_FIELD);
2756 }
2757
2758 static void drawHallOfFameList(int first_entry, int highlight_position)
2759 {
2760   int i;
2761
2762   SetMainBackgroundImage(IMG_BACKGROUND_SCORES);
2763   ClearWindow();
2764
2765   DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame");
2766   DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2,
2767                     "HighScores of Level %d", level_nr);
2768
2769   for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
2770   {
2771     int entry = first_entry + i;
2772     boolean active = (entry == highlight_position);
2773     int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1);
2774     int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2);
2775     int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3);
2776     int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4);
2777     int dx1 = 3 * getFontWidth(font_nr1);
2778     int dx2 = dx1 + getFontWidth(font_nr1);
2779     int dx3 = dx2 + 25 * getFontWidth(font_nr3);
2780     int sy = mSY + 64 + i * 32;
2781
2782     DrawText(mSX, sy, int2str(entry + 1, 3), font_nr1);
2783     DrawText(mSX + dx1, sy, ".", font_nr1);
2784     DrawText(mSX + dx2, sy, ".........................", font_nr3);
2785
2786     if (!strEqual(highscore[entry].Name, EMPTY_PLAYER_NAME))
2787       DrawText(mSX + dx2, sy, highscore[entry].Name, font_nr2);
2788
2789     DrawText(mSX + dx3, sy, int2str(highscore[entry].Score, 5), font_nr4);
2790   }
2791
2792   redraw_mask |= REDRAW_FIELD;
2793 }
2794
2795 void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
2796 {
2797   static int first_entry = 0;
2798   static int highlight_position = 0;
2799   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
2800
2801   if (button == MB_MENU_INITIALIZE)
2802   {
2803     first_entry = 0;
2804     highlight_position = mx;
2805     drawHallOfFameList(first_entry, highlight_position);
2806
2807     return;
2808   }
2809
2810   if (ABS(dy) == SCROLL_PAGE)           /* handle scrolling one page */
2811     step = NUM_MENU_ENTRIES_ON_SCREEN - 1;
2812
2813   if (dy < 0)
2814   {
2815     if (first_entry > 0)
2816     {
2817       first_entry -= step;
2818       if (first_entry < 0)
2819         first_entry = 0;
2820
2821       drawHallOfFameList(first_entry, highlight_position);
2822     }
2823   }
2824   else if (dy > 0)
2825   {
2826     if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES)
2827     {
2828       first_entry += step;
2829       if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
2830         first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
2831
2832       drawHallOfFameList(first_entry, highlight_position);
2833     }
2834   }
2835   else if (button == MB_MENU_LEAVE)
2836   {
2837     FadeSound(SND_BACKGROUND_SCORES);
2838
2839     game_status = GAME_MODE_MAIN;
2840
2841     DrawMainMenu();
2842   }
2843   else if (button == MB_MENU_CHOICE)
2844   {
2845     FadeSound(SND_BACKGROUND_SCORES);
2846     FadeOut(REDRAW_FIELD);
2847
2848     game_status = GAME_MODE_MAIN;
2849
2850     DrawAndFadeInMainMenu(REDRAW_FIELD);
2851   }
2852
2853   if (game_status == GAME_MODE_SCORES)
2854     PlayMenuSoundIfLoop();
2855
2856   DoAnimation();
2857 }
2858
2859
2860 /* ========================================================================= */
2861 /* setup screen functions                                                    */
2862 /* ========================================================================= */
2863
2864 static struct TokenInfo *setup_info;
2865 static int num_setup_info;
2866
2867 static char *screen_mode_text;
2868 static char *graphics_set_name;
2869 static char *sounds_set_name;
2870 static char *music_set_name;
2871
2872 static void execSetupMain()
2873 {
2874   setup_mode = SETUP_MODE_MAIN;
2875   DrawSetupScreen();
2876 }
2877
2878 static void execSetupGame()
2879 {
2880   setup_mode = SETUP_MODE_GAME;
2881   DrawSetupScreen();
2882 }
2883
2884 static void execSetupEditor()
2885 {
2886   setup_mode = SETUP_MODE_EDITOR;
2887   DrawSetupScreen();
2888 }
2889
2890 static void execSetupGraphics()
2891 {
2892   if (video.fullscreen_available && screen_modes == NULL)
2893   {
2894     int i;
2895
2896     for (i = 0; video.fullscreen_modes[i].width != -1; i++)
2897     {
2898       TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED);
2899       char identifier[32], name[32];
2900       int x = video.fullscreen_modes[i].width;
2901       int y = video.fullscreen_modes[i].height;
2902       int xx, yy;
2903
2904       get_aspect_ratio_from_screen_mode(&video.fullscreen_modes[i], &xx, &yy);
2905
2906       ti->node_top = &screen_modes;
2907       ti->sort_priority = x * 10000 + y;
2908
2909       sprintf(identifier, "%dx%d", x, y);
2910       sprintf(name,     "%d x %d [%d:%d]", x, y, xx, yy);
2911
2912       setString(&ti->identifier, identifier);
2913       setString(&ti->name, name);
2914       setString(&ti->name_sorting, name);
2915       setString(&ti->infotext, "Fullscreen Mode");
2916
2917       pushTreeInfo(&screen_modes, ti);
2918     }
2919
2920     /* sort fullscreen modes to start with lowest available screen resolution */
2921     sortTreeInfo(&screen_modes);
2922
2923     /* set current screen mode for fullscreen mode to configured setup value */
2924     screen_mode_current = getTreeInfoFromIdentifier(screen_modes,
2925                                                     setup.fullscreen_mode);
2926
2927     /* if that fails, set current screen mode to reliable default value */
2928     if (screen_mode_current == NULL)
2929       screen_mode_current = getTreeInfoFromIdentifier(screen_modes,
2930                                                       DEFAULT_FULLSCREEN_MODE);
2931
2932     /* if that also fails, set current screen mode to first available mode */
2933     if (screen_mode_current == NULL)
2934       screen_mode_current = screen_modes;
2935
2936     if (screen_mode_current == NULL)
2937       video.fullscreen_available = FALSE;
2938   }
2939
2940   if (video.fullscreen_available)
2941   {
2942     setup.fullscreen_mode = screen_mode_current->identifier;
2943
2944     /* needed for displaying screen mode name instead of identifier */
2945     screen_mode_text = screen_mode_current->name;
2946   }
2947
2948   setup_mode = SETUP_MODE_GRAPHICS;
2949   DrawSetupScreen();
2950 }
2951
2952 static void execSetupChooseScreenMode()
2953 {
2954   if (!video.fullscreen_available)
2955     return;
2956
2957   setup_mode = SETUP_MODE_CHOOSE_SCREEN_MODE;
2958   DrawSetupScreen();
2959 }
2960
2961 static void execSetupSound()
2962 {
2963   setup_mode = SETUP_MODE_SOUND;
2964   DrawSetupScreen();
2965 }
2966
2967 static void execSetupArtwork()
2968 {
2969   setup.graphics_set = artwork.gfx_current->identifier;
2970   setup.sounds_set = artwork.snd_current->identifier;
2971   setup.music_set = artwork.mus_current->identifier;
2972
2973   /* needed if last screen (setup choice) changed graphics, sounds or music */
2974   ReloadCustomArtwork(0);
2975
2976   /* needed for displaying artwork name instead of artwork identifier */
2977   graphics_set_name = artwork.gfx_current->name;
2978   sounds_set_name = artwork.snd_current->name;
2979   music_set_name = artwork.mus_current->name;
2980
2981   setup_mode = SETUP_MODE_ARTWORK;
2982   DrawSetupScreen();
2983 }
2984
2985 static void execSetupChooseGraphics()
2986 {
2987   setup_mode = SETUP_MODE_CHOOSE_GRAPHICS;
2988   DrawSetupScreen();
2989 }
2990
2991 static void execSetupChooseSounds()
2992 {
2993   setup_mode = SETUP_MODE_CHOOSE_SOUNDS;
2994   DrawSetupScreen();
2995 }
2996
2997 static void execSetupChooseMusic()
2998 {
2999   setup_mode = SETUP_MODE_CHOOSE_MUSIC;
3000   DrawSetupScreen();
3001 }
3002
3003 static void execSetupInput()
3004 {
3005   setup_mode = SETUP_MODE_INPUT;
3006   DrawSetupScreen();
3007 }
3008
3009 static void execSetupShortcut1()
3010 {
3011   setup_mode = SETUP_MODE_SHORTCUT_1;
3012   DrawSetupScreen();
3013 }
3014
3015 static void execSetupShortcut2()
3016 {
3017   setup_mode = SETUP_MODE_SHORTCUT_2;
3018   DrawSetupScreen();
3019 }
3020
3021 static void execExitSetup()
3022 {
3023   game_status = GAME_MODE_MAIN;
3024   DrawMainMenu();
3025 }
3026
3027 static void execSaveAndExitSetup()
3028 {
3029   SaveSetup();
3030   execExitSetup();
3031 }
3032
3033 static struct TokenInfo setup_info_main[] =
3034 {
3035   { TYPE_ENTER_MENU,    execSetupGame,          "Game & Menu"           },
3036   { TYPE_ENTER_MENU,    execSetupEditor,        "Editor"                },
3037   { TYPE_ENTER_MENU,    execSetupGraphics,      "Graphics"              },
3038   { TYPE_ENTER_MENU,    execSetupSound,         "Sound & Music"         },
3039   { TYPE_ENTER_MENU,    execSetupArtwork,       "Custom Artwork"        },
3040   { TYPE_ENTER_MENU,    execSetupInput,         "Input Devices"         },
3041   { TYPE_ENTER_MENU,    execSetupShortcut1,     "Key Shortcuts 1"       },
3042   { TYPE_ENTER_MENU,    execSetupShortcut2,     "Key Shortcuts 2"       },
3043   { TYPE_EMPTY,         NULL,                   ""                      },
3044   { TYPE_LEAVE_MENU,    execExitSetup,          "Exit"                  },
3045   { TYPE_LEAVE_MENU,    execSaveAndExitSetup,   "Save and Exit"         },
3046
3047   { 0,                  NULL,                   NULL                    }
3048 };
3049
3050 static struct TokenInfo setup_info_game[] =
3051 {
3052   { TYPE_SWITCH,        &setup.team_mode,       "Team-Mode (Multi-Player):" },
3053   { TYPE_YES_NO,        &setup.input_on_focus,  "Only Move Focussed Player:" },
3054   { TYPE_SWITCH,        &setup.handicap,        "Handicap:"             },
3055   { TYPE_SWITCH,        &setup.skip_levels,     "Skip Unsolved Levels:" },
3056   { TYPE_SWITCH,        &setup.time_limit,      "Time Limit:"           },
3057   { TYPE_SWITCH,        &setup.autorecord,      "Auto-Record Tapes:"    },
3058   { TYPE_EMPTY,         NULL,                   ""                      },
3059   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3060
3061   { 0,                  NULL,                   NULL                    }
3062 };
3063
3064 static struct TokenInfo setup_info_editor[] =
3065 {
3066 #if 0
3067   { TYPE_SWITCH,        &setup.editor.el_boulderdash,   "Boulder Dash:" },
3068   { TYPE_SWITCH,        &setup.editor.el_emerald_mine,  "Emerald Mine:" },
3069   { TYPE_SWITCH, &setup.editor.el_emerald_mine_club,    "Emerald Mine Club:" },
3070   { TYPE_SWITCH,        &setup.editor.el_more,          "Rocks'n'Diamonds:" },
3071   { TYPE_SWITCH,        &setup.editor.el_sokoban,       "Sokoban:"      },
3072   { TYPE_SWITCH,        &setup.editor.el_supaplex,      "Supaplex:"     },
3073   { TYPE_SWITCH,        &setup.editor.el_diamond_caves, "Diamond Caves II:" },
3074   { TYPE_SWITCH,        &setup.editor.el_dx_boulderdash,"DX-Boulderdash:" },
3075 #endif
3076   { TYPE_SWITCH,        &setup.editor.el_chars,         "Text Characters:" },
3077   { TYPE_SWITCH,        &setup.editor.el_custom,  "Custom & Group Elements:" },
3078 #if 0
3079   { TYPE_SWITCH,        &setup.editor.el_headlines,     "Headlines:"    },
3080 #endif
3081   { TYPE_SWITCH, &setup.editor.el_user_defined, "User defined element list:" },
3082   { TYPE_SWITCH,        &setup.editor.el_dynamic,  "Dynamic level elements:" },
3083   { TYPE_EMPTY,         NULL,                   ""                      },
3084 #if 0
3085   { TYPE_SWITCH,        &setup.editor.el_by_game,   "Show elements by game:" },
3086   { TYPE_SWITCH,        &setup.editor.el_by_type,   "Show elements by type:" },
3087   { TYPE_EMPTY,         NULL,                   ""                      },
3088 #endif
3089   { TYPE_SWITCH, &setup.editor.show_element_token,      "Show element token:" },
3090   { TYPE_EMPTY,         NULL,                   ""                      },
3091   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3092
3093   { 0,                  NULL,                   NULL                    }
3094 };
3095
3096 static struct TokenInfo setup_info_graphics[] =
3097 {
3098   { TYPE_SWITCH,        &setup.fullscreen,      "Fullscreen:"           },
3099   { TYPE_ENTER_LIST,    execSetupChooseScreenMode, "Fullscreen Mode:"   },
3100   { TYPE_STRING,        &screen_mode_text,      ""                      },
3101   { TYPE_SWITCH,        &setup.scroll_delay,    "Delayed Scrolling:"    },
3102 #if 0
3103   { TYPE_SWITCH,        &setup.soft_scrolling,  "Soft Scrolling:"       },
3104   { TYPE_SWITCH,        &setup.double_buffering,"Double-Buffering:"     },
3105 #endif
3106   { TYPE_SWITCH,        &setup.fade_screens,    "Fade Screens:"         },
3107   { TYPE_SWITCH,        &setup.quick_switch,    "Quick Player Focus Switch:" },
3108   { TYPE_SWITCH,        &setup.quick_doors,     "Quick Menu Doors:"     },
3109   { TYPE_SWITCH,        &setup.show_titlescreen,"Show Title Screens:"   },
3110   { TYPE_SWITCH,        &setup.toons,           "Show Toons:"           },
3111   { TYPE_ECS_AGA,       &setup.prefer_aga_graphics,"EMC graphics preference:" },
3112   { TYPE_EMPTY,         NULL,                   ""                      },
3113   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3114
3115   { 0,                  NULL,                   NULL                    }
3116 };
3117
3118 static struct TokenInfo setup_info_sound[] =
3119 {
3120   { TYPE_SWITCH,        &setup.sound_simple,    "Sound Effects (Normal):"  },
3121   { TYPE_SWITCH,        &setup.sound_loops,     "Sound Effects (Looping):" },
3122   { TYPE_SWITCH,        &setup.sound_music,     "Music:"                },
3123   { TYPE_EMPTY,         NULL,                   ""                      },
3124   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3125
3126   { 0,                  NULL,                   NULL                    }
3127 };
3128
3129 static struct TokenInfo setup_info_artwork[] =
3130 {
3131   { TYPE_ENTER_LIST,    execSetupChooseGraphics,"Custom Graphics:"      },
3132   { TYPE_STRING,        &graphics_set_name,     ""                      },
3133   { TYPE_ENTER_LIST,    execSetupChooseSounds,  "Custom Sounds:"        },
3134   { TYPE_STRING,        &sounds_set_name,       ""                      },
3135   { TYPE_ENTER_LIST,    execSetupChooseMusic,   "Custom Music:"         },
3136   { TYPE_STRING,        &music_set_name,        ""                      },
3137   { TYPE_EMPTY,         NULL,                   ""                      },
3138 #if 1
3139   { TYPE_YES_NO, &setup.override_level_graphics,"Override Level Graphics:" },
3140   { TYPE_YES_NO, &setup.override_level_sounds,  "Override Level Sounds:"   },
3141   { TYPE_YES_NO, &setup.override_level_music,   "Override Level Music:"    },
3142 #else
3143   { TYPE_STRING,        NULL,                   "Override Level Artwork:"},
3144   { TYPE_YES_NO,        &setup.override_level_graphics, "Graphics:"     },
3145   { TYPE_YES_NO,        &setup.override_level_sounds,   "Sounds:"       },
3146   { TYPE_YES_NO,        &setup.override_level_music,    "Music:"        },
3147 #endif
3148   { TYPE_EMPTY,         NULL,                   ""                      },
3149   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3150
3151   { 0,                  NULL,                   NULL                    }
3152 };
3153
3154 static struct TokenInfo setup_info_input[] =
3155 {
3156   { TYPE_SWITCH,        NULL,                   "Player:"               },
3157   { TYPE_SWITCH,        NULL,                   "Device:"               },
3158   { TYPE_ENTER_MENU,    NULL,                   ""                      },
3159   { TYPE_EMPTY,         NULL,                   ""                      },
3160   { TYPE_EMPTY,         NULL,                   ""                      },
3161   { TYPE_EMPTY,         NULL,                   ""                      },
3162   { TYPE_EMPTY,         NULL,                   ""                      },
3163   { TYPE_EMPTY,         NULL,                   ""                      },
3164   { TYPE_EMPTY,         NULL,                   ""                      },
3165   { TYPE_EMPTY,         NULL,                   ""                      },
3166   { TYPE_EMPTY,         NULL,                   ""                      },
3167   { TYPE_EMPTY,         NULL,                   ""                      },
3168   { TYPE_EMPTY,         NULL,                   ""                      },
3169   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3170
3171   { 0,                  NULL,                   NULL                    }
3172 };
3173
3174 static struct TokenInfo setup_info_shortcut_1[] =
3175 {
3176   { TYPE_KEYTEXT,       NULL,           "Quick Save Game to Tape:",     },
3177   { TYPE_KEY,           &setup.shortcut.save_game, ""                   },
3178   { TYPE_KEYTEXT,       NULL,           "Quick Load Game from Tape:",   },
3179   { TYPE_KEY,           &setup.shortcut.load_game, ""                   },
3180   { TYPE_KEYTEXT,       NULL,           "Start Game & Toggle Pause:",   },
3181   { TYPE_KEY,           &setup.shortcut.toggle_pause, ""                },
3182   { TYPE_EMPTY,         NULL,                   ""                      },
3183   { TYPE_YES_NO,        &setup.ask_on_escape,   "Ask on 'Esc' Key:"     },
3184   { TYPE_YES_NO, &setup.ask_on_escape_editor,   "Ask on 'Esc' Key (Editor):" },
3185   { TYPE_EMPTY,         NULL,                   ""                      },
3186   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3187
3188   { 0,                  NULL,                   NULL                    }
3189 };
3190
3191 static struct TokenInfo setup_info_shortcut_2[] =
3192 {
3193   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 1:",       },
3194   { TYPE_KEY,           &setup.shortcut.focus_player[0], ""             },
3195   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 2:",       },
3196   { TYPE_KEY,           &setup.shortcut.focus_player[1], ""             },
3197   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 3:",       },
3198   { TYPE_KEY,           &setup.shortcut.focus_player[2], ""             },
3199   { TYPE_KEYTEXT,       NULL,           "Set Focus to Player 4:",       },
3200   { TYPE_KEY,           &setup.shortcut.focus_player[3], ""             },
3201   { TYPE_KEYTEXT,       NULL,           "Set Focus to All Players:",    },
3202   { TYPE_KEY,           &setup.shortcut.focus_player_all, ""            },
3203   { TYPE_EMPTY,         NULL,                   ""                      },
3204   { TYPE_LEAVE_MENU,    execSetupMain,          "Back"                  },
3205
3206   { 0,                  NULL,                   NULL                    }
3207 };
3208
3209 static Key getSetupKey()
3210 {
3211   Key key = KSYM_UNDEFINED;
3212   boolean got_key_event = FALSE;
3213
3214   while (!got_key_event)
3215   {
3216     if (PendingEvent())         /* got event */
3217     {
3218       Event event;
3219
3220       NextEvent(&event);
3221
3222       switch(event.type)
3223       {
3224         case EVENT_KEYPRESS:
3225           {
3226             key = GetEventKey((KeyEvent *)&event, TRUE);
3227
3228             /* press 'Escape' or 'Enter' to keep the existing key binding */
3229             if (key == KSYM_Escape || key == KSYM_Return)
3230               key = KSYM_UNDEFINED;     /* keep old value */
3231
3232             got_key_event = TRUE;
3233           }
3234           break;
3235
3236         case EVENT_KEYRELEASE:
3237           key_joystick_mapping = 0;
3238           break;
3239
3240         default:
3241           HandleOtherEvents(&event);
3242           break;
3243       }
3244     }
3245
3246     DoAnimation();
3247     BackToFront();
3248
3249     /* don't eat all CPU time */
3250     Delay(10);
3251   }
3252
3253   return key;
3254 }
3255
3256 static int getSetupTextFont(int type)
3257 {
3258   if (type & (TYPE_SWITCH |
3259               TYPE_YES_NO |
3260               TYPE_STRING |
3261               TYPE_ECS_AGA |
3262               TYPE_KEYTEXT |
3263               TYPE_ENTER_LIST))
3264     return FONT_MENU_2;
3265   else
3266     return FONT_MENU_1;
3267 }
3268
3269 static int getSetupValueFont(int type, void *value)
3270 {
3271   if (type & TYPE_KEY)
3272     return (type & TYPE_QUERY ? FONT_INPUT_1_ACTIVE : FONT_VALUE_1);
3273   else if (type & TYPE_STRING)
3274     return FONT_VALUE_2;
3275   else if (type & TYPE_ECS_AGA)
3276     return FONT_VALUE_1;
3277   else if (type & TYPE_BOOLEAN_STYLE)
3278     return (*(boolean *)value ? FONT_OPTION_ON : FONT_OPTION_OFF);
3279   else
3280     return FONT_VALUE_1;
3281 }
3282
3283 static void drawSetupValue(int pos)
3284 {
3285   boolean font_draw_xoffset_modified = FALSE;
3286   int font_draw_xoffset_old = -1;
3287   int xpos = MENU_SCREEN_VALUE_XPOS;
3288   int ypos = MENU_SCREEN_START_YPOS + pos;
3289   int startx = mSX + xpos * 32;
3290   int starty = mSY + ypos * 32;
3291   int font_nr, font_width;
3292   int type = setup_info[pos].type;
3293   void *value = setup_info[pos].value;
3294   char *value_string = getSetupValue(type, value);
3295   int i;
3296
3297   if (value_string == NULL)
3298     return;
3299
3300   if (type & TYPE_KEY)
3301   {
3302     xpos = MENU_SCREEN_START_XPOS;
3303
3304     if (type & TYPE_QUERY)
3305     {
3306       value_string = "<press key>";
3307     }
3308   }
3309   else if (type & TYPE_STRING)
3310   {
3311     int max_value_len = (SCR_FIELDX - 2) * 2;
3312
3313     xpos = MENU_SCREEN_START_XPOS;
3314
3315     if (strlen(value_string) > max_value_len)
3316       value_string[max_value_len] = '\0';
3317   }
3318
3319   startx = mSX + xpos * 32;
3320   starty = mSY + ypos * 32;
3321   font_nr = getSetupValueFont(type, value);
3322   font_width = getFontWidth(font_nr);
3323
3324   /* downward compatibility correction for Juergen Bonhagen's menu settings */
3325   if (setup_mode != SETUP_MODE_INPUT)
3326   {
3327     int check_font_nr = FONT_OPTION_ON; /* known font that needs correction */
3328     int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
3329     int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset;
3330     int text_startx = mSX + MENU_SCREEN_START_XPOS * 32;
3331     int text_font_nr = getSetupTextFont(FONT_MENU_2);
3332     int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset;
3333     int text_width = MAX_MENU_TEXT_LENGTH_MEDIUM * getFontWidth(text_font_nr);
3334     boolean correct_font_draw_xoffset = FALSE;
3335
3336     if (xpos == MENU_SCREEN_START_XPOS &&
3337         startx + font1_xoffset < text_startx + text_font_xoffset)
3338       correct_font_draw_xoffset = TRUE;
3339
3340     if (xpos == MENU_SCREEN_VALUE_XPOS &&
3341         startx + font2_xoffset < text_startx + text_width + text_font_xoffset)
3342       correct_font_draw_xoffset = TRUE;
3343
3344     /* check if setup value would overlap with setup text when printed */
3345     /* (this can happen for extreme/wrong values for font draw offset) */
3346     if (correct_font_draw_xoffset)
3347     {
3348       font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset;
3349       font_draw_xoffset_modified = TRUE;
3350
3351       if (type & TYPE_KEY)
3352         getFontBitmapInfo(font_nr)->draw_xoffset += 2 * getFontWidth(font_nr);
3353       else if (!(type & TYPE_STRING))
3354         getFontBitmapInfo(font_nr)->draw_xoffset = text_font_xoffset + 20 -
3355           MAX_MENU_TEXT_LENGTH_MEDIUM * (16 - getFontWidth(text_font_nr));
3356     }
3357   }
3358
3359   for (i = 0; i <= MENU_SCREEN_MAX_XPOS - xpos; i++)
3360     DrawText(startx + i * font_width, starty, " ", font_nr);
3361
3362   DrawText(startx, starty, value_string, font_nr);
3363
3364   if (font_draw_xoffset_modified)
3365     getFontBitmapInfo(font_nr)->draw_xoffset = font_draw_xoffset_old;
3366 }
3367
3368 static void changeSetupValue(int pos)
3369 {
3370   if (setup_info[pos].type & TYPE_BOOLEAN_STYLE)
3371   {
3372     *(boolean *)setup_info[pos].value ^= TRUE;
3373   }
3374   else if (setup_info[pos].type & TYPE_KEY)
3375   {
3376     Key key;
3377
3378     setup_info[pos].type |= TYPE_QUERY;
3379     drawSetupValue(pos);
3380     setup_info[pos].type &= ~TYPE_QUERY;
3381
3382     key = getSetupKey();
3383     if (key != KSYM_UNDEFINED)
3384       *(Key *)setup_info[pos].value = key;
3385   }
3386
3387   drawSetupValue(pos);
3388 }
3389
3390 static void DrawCursorAndText_Setup(int pos, boolean active)
3391 {
3392   int xpos = MENU_SCREEN_START_XPOS;
3393   int ypos = MENU_SCREEN_START_YPOS + pos;
3394   int font_nr = getSetupTextFont(setup_info[pos].type);
3395
3396   if (setup_info == setup_info_input)
3397     font_nr = FONT_MENU_1;
3398
3399   if (active)
3400     font_nr = FONT_ACTIVE(font_nr);
3401
3402   DrawText(mSX + xpos * 32, mSY + ypos * 32, setup_info[pos].text, font_nr);
3403
3404   if (setup_info[pos].type & ~TYPE_SKIP_ENTRY)
3405     drawCursor(pos, active);
3406 }
3407
3408 static void DrawSetupScreen_Generic()
3409 {
3410   char *title_string = NULL;
3411   int i;
3412
3413   UnmapAllGadgets();
3414   CloseDoor(DOOR_CLOSE_2);
3415
3416   ClearWindow();
3417
3418   if (setup_mode == SETUP_MODE_MAIN)
3419   {
3420     setup_info = setup_info_main;
3421     title_string = "Setup";
3422   }
3423   else if (setup_mode == SETUP_MODE_GAME)
3424   {
3425     setup_info = setup_info_game;
3426     title_string = "Setup Game";
3427   }
3428   else if (setup_mode == SETUP_MODE_EDITOR)
3429   {
3430     setup_info = setup_info_editor;
3431     title_string = "Setup Editor";
3432   }
3433   else if (setup_mode == SETUP_MODE_GRAPHICS)
3434   {
3435     setup_info = setup_info_graphics;
3436     title_string = "Setup Graphics";
3437   }
3438   else if (setup_mode == SETUP_MODE_SOUND)
3439   {
3440     setup_info = setup_info_sound;
3441     title_string = "Setup Sound";
3442   }
3443   else if (setup_mode == SETUP_MODE_ARTWORK)
3444   {
3445     setup_info = setup_info_artwork;
3446     title_string = "Custom Artwork";
3447   }
3448   else if (setup_mode == SETUP_MODE_SHORTCUT_1)
3449   {
3450     setup_info = setup_info_shortcut_1;
3451     title_string = "Setup Shortcuts";
3452   }
3453   else if (setup_mode == SETUP_MODE_SHORTCUT_2)
3454   {
3455     setup_info = setup_info_shortcut_2;
3456     title_string = "Setup Shortcuts";
3457   }
3458
3459   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, title_string);
3460
3461   num_setup_info = 0;
3462   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
3463   {
3464     void *value_ptr = setup_info[i].value;
3465 #if 1
3466 #else
3467     int xpos = MENU_SCREEN_START_XPOS;
3468     int ypos = MENU_SCREEN_START_YPOS + i;
3469     int font_nr;
3470 #endif
3471
3472     /* set some entries to "unchangeable" according to other variables */
3473     if ((value_ptr == &setup.sound_simple && !audio.sound_available) ||
3474         (value_ptr == &setup.sound_loops  && !audio.loops_available) ||
3475         (value_ptr == &setup.sound_music  && !audio.music_available) ||
3476         (value_ptr == &setup.fullscreen   && !video.fullscreen_available) ||
3477         (value_ptr == &screen_mode_text   && !video.fullscreen_available))
3478       setup_info[i].type |= TYPE_GHOSTED;
3479
3480     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
3481       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
3482     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
3483       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
3484     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
3485       initCursor(i, IMG_MENU_BUTTON);
3486
3487 #if 1
3488     DrawCursorAndText_Setup(i, FALSE);
3489 #else
3490     font_nr = getSetupTextFont(setup_info[i].type);
3491
3492     DrawText(mSX + xpos * 32, mSY + ypos * 32, setup_info[i].text, font_nr);
3493 #endif
3494
3495     if (setup_info[i].type & TYPE_VALUE)
3496       drawSetupValue(i);
3497
3498     num_setup_info++;
3499   }
3500
3501 #if 0
3502   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3503                     "Joysticks deactivated in setup menu");
3504 #endif
3505
3506   FadeToFront();
3507   InitAnimation();
3508   HandleSetupScreen_Generic(0, 0, 0, 0, MB_MENU_INITIALIZE);
3509 }
3510
3511 void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button)
3512 {
3513   static int choice_store[MAX_SETUP_MODES];
3514   int choice = choice_store[setup_mode];        /* always starts with 0 */
3515   int x = 0;
3516   int y = choice;
3517
3518   if (button == MB_MENU_INITIALIZE)
3519   {
3520     /* advance to first valid menu entry */
3521     while (choice < num_setup_info &&
3522            setup_info[choice].type & TYPE_SKIP_ENTRY)
3523       choice++;
3524     choice_store[setup_mode] = choice;
3525
3526 #if 1
3527     DrawCursorAndText_Setup(choice, TRUE);
3528 #else
3529     drawCursor(choice, TRUE);
3530 #endif
3531
3532     return;
3533   }
3534   else if (button == MB_MENU_LEAVE)
3535   {
3536     for (y = 0; y < num_setup_info; y++)
3537     {
3538       if (setup_info[y].type & TYPE_LEAVE_MENU)
3539       {
3540         void (*menu_callback_function)(void) = setup_info[y].value;
3541
3542         menu_callback_function();
3543
3544         break;  /* absolutely needed because function changes 'setup_info'! */
3545       }
3546     }
3547
3548     return;
3549   }
3550
3551   if (mx || my)         /* mouse input */
3552   {
3553     x = (mx - mSX) / 32;
3554     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3555   }
3556   else if (dx || dy)    /* keyboard input */
3557   {
3558     if (dx)
3559     {
3560       int menu_navigation_type = (dx < 0 ? TYPE_LEAVE : TYPE_ENTER);
3561
3562       if (setup_info[choice].type & menu_navigation_type ||
3563           setup_info[choice].type & TYPE_BOOLEAN_STYLE)
3564         button = MB_MENU_CHOICE;
3565     }
3566     else if (dy)
3567       y = choice + dy;
3568
3569     /* jump to next non-empty menu entry (up or down) */
3570     while (y > 0 && y < num_setup_info - 1 &&
3571            setup_info[y].type & TYPE_SKIP_ENTRY)
3572       y += dy;
3573   }
3574
3575   if (IN_VIS_FIELD(x, y) && y >= 0 && y < num_setup_info)
3576   {
3577     if (button)
3578     {
3579       if (y != choice && setup_info[y].type & ~TYPE_SKIP_ENTRY)
3580       {
3581 #if 1
3582         DrawCursorAndText_Setup(choice, FALSE);
3583         DrawCursorAndText_Setup(y, TRUE);
3584 #else
3585         drawCursor(choice, FALSE);
3586         drawCursor(y, TRUE);
3587 #endif
3588
3589         choice = choice_store[setup_mode] = y;
3590       }
3591     }
3592     else if (!(setup_info[y].type & TYPE_GHOSTED))
3593     {
3594       /* when selecting key headline, execute function for key value change */
3595       if (setup_info[y].type & TYPE_KEYTEXT &&
3596           setup_info[y + 1].type & TYPE_KEY)
3597         y++;
3598
3599       /* when selecting string value, execute function for list selection */
3600       if (setup_info[y].type & TYPE_STRING && y > 0 &&
3601           setup_info[y - 1].type & TYPE_ENTER_LIST)
3602         y--;
3603
3604       if (setup_info[y].type & TYPE_ENTER_OR_LEAVE)
3605       {
3606         void (*menu_callback_function)(void) = setup_info[y].value;
3607
3608         menu_callback_function();
3609       }
3610       else
3611       {
3612         if (setup_info[y].type & TYPE_VALUE)
3613           changeSetupValue(y);
3614       }
3615     }
3616   }
3617 }
3618
3619 void DrawSetupScreen_Input()
3620 {
3621 #if 1
3622   int i;
3623 #endif
3624
3625   ClearWindow();
3626
3627 #if 1
3628   setup_info = setup_info_input;
3629 #endif
3630
3631   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Setup Input");
3632
3633 #if 1
3634   for (i = 0; setup_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++)
3635   {
3636     if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST))
3637       initCursor(i, IMG_MENU_BUTTON_ENTER_MENU);
3638     else if (setup_info[i].type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST))
3639       initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU);
3640     else if (setup_info[i].type & ~TYPE_SKIP_ENTRY)
3641       initCursor(i, IMG_MENU_BUTTON);
3642
3643     DrawCursorAndText_Setup(i, FALSE);
3644   }
3645 #else
3646   initCursor(0,  IMG_MENU_BUTTON);
3647   initCursor(1,  IMG_MENU_BUTTON);
3648   initCursor(2,  IMG_MENU_BUTTON_ENTER_MENU);
3649   initCursor(13, IMG_MENU_BUTTON_LEAVE_MENU);
3650
3651   DrawText(mSX + 32, mSY +  2 * 32, "Player:", FONT_MENU_1);
3652   DrawText(mSX + 32, mSY +  3 * 32, "Device:", FONT_MENU_1);
3653   DrawText(mSX + 32, mSY + 15 * 32, "Back",   FONT_MENU_1);
3654 #endif
3655
3656 #if 0
3657   DeactivateJoystickForCalibration();
3658 #endif
3659 #if 1
3660   DrawTextSCentered(SYSIZE - 20, FONT_TEXT_4,
3661                     "Joysticks deactivated on this screen");
3662 #endif
3663
3664   /* create gadgets for setup input menu screen */
3665   FreeScreenGadgets();
3666   CreateScreenGadgets();
3667
3668   /* map gadgets for setup input menu screen */
3669   MapScreenMenuGadgets(SCREEN_MASK_INPUT);
3670
3671   HandleSetupScreen_Input(0, 0, 0, 0, MB_MENU_INITIALIZE);
3672   FadeToFront();
3673   InitAnimation();
3674 }
3675
3676 static void setJoystickDeviceToNr(char *device_name, int device_nr)
3677 {
3678   if (device_name == NULL)
3679     return;
3680
3681   if (device_nr < 0 || device_nr >= MAX_PLAYERS)
3682     device_nr = 0;
3683
3684   if (strlen(device_name) > 1)
3685   {
3686     char c1 = device_name[strlen(device_name) - 1];
3687     char c2 = device_name[strlen(device_name) - 2];
3688
3689     if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9'))
3690       device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10);
3691   }
3692   else
3693     strncpy(device_name, getDeviceNameFromJoystickNr(device_nr),
3694             strlen(device_name));
3695 }
3696
3697 static void drawPlayerSetupInputInfo(int player_nr, boolean active)
3698 {
3699   int i;
3700   static struct SetupKeyboardInfo custom_key;
3701   static struct
3702   {
3703     Key *key;
3704     char *text;
3705   } custom[] =
3706   {
3707     { &custom_key.left,  "Joystick Left"  },
3708     { &custom_key.right, "Joystick Right" },
3709     { &custom_key.up,    "Joystick Up"    },
3710     { &custom_key.down,  "Joystick Down"  },
3711     { &custom_key.snap,  "Button 1"       },
3712     { &custom_key.drop,  "Button 2"       }
3713   };
3714   static char *joystick_name[MAX_PLAYERS] =
3715   {
3716     "Joystick1",
3717     "Joystick2",
3718     "Joystick3",
3719     "Joystick4"
3720   };
3721   int text_font_nr = (active ? FONT_MENU_1_ACTIVE : FONT_MENU_1);
3722
3723   InitJoysticks();
3724
3725   custom_key = setup.input[player_nr].key;
3726
3727   DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1),
3728            FONT_INPUT_1_ACTIVE);
3729
3730   ClearRectangleOnBackground(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3731                              TILEX, TILEY);
3732   DrawGraphicThruMaskExt(drawto, mSX + 8 * TILEX, mSY + 2 * TILEY,
3733                          PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
3734
3735   if (setup.input[player_nr].use_joystick)
3736   {
3737     char *device_name = setup.input[player_nr].joy.device_name;
3738     char *text = joystick_name[getJoystickNrFromDeviceName(device_name)];
3739     int font_nr = (joystick.fd[player_nr] < 0 ? FONT_VALUE_OLD : FONT_VALUE_1);
3740
3741     DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr);
3742     DrawText(mSX + 32, mSY + 4 * 32, "Calibrate", text_font_nr);
3743   }
3744   else
3745   {
3746     DrawText(mSX + 8 * 32, mSY + 3 * 32, "Keyboard ", FONT_VALUE_1);
3747     DrawText(mSX + 1 * 32, mSY + 4 * 32, "Customize", text_font_nr);
3748   }
3749
3750   DrawText(mSX + 32, mSY + 5 * 32, "Actual Settings:", FONT_MENU_1);
3751
3752   drawCursorXY(1, 4, IMG_MENU_BUTTON_LEFT);
3753   drawCursorXY(1, 5, IMG_MENU_BUTTON_RIGHT);
3754   drawCursorXY(1, 6, IMG_MENU_BUTTON_UP);
3755   drawCursorXY(1, 7, IMG_MENU_BUTTON_DOWN);
3756
3757   DrawText(mSX + 2 * 32, mSY +  6 * 32, ":", FONT_VALUE_OLD);
3758   DrawText(mSX + 2 * 32, mSY +  7 * 32, ":", FONT_VALUE_OLD);
3759   DrawText(mSX + 2 * 32, mSY +  8 * 32, ":", FONT_VALUE_OLD);
3760   DrawText(mSX + 2 * 32, mSY +  9 * 32, ":", FONT_VALUE_OLD);
3761   DrawText(mSX + 1 * 32, mSY + 10 * 32, "Snap Field:", FONT_VALUE_OLD);
3762   DrawText(mSX + 1 * 32, mSY + 12 * 32, "Drop Element:", FONT_VALUE_OLD);
3763
3764   for (i = 0; i < 6; i++)
3765   {
3766     int ypos = 6 + i + (i > 3 ? i-3 : 0);
3767
3768     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3769              "              ", FONT_VALUE_1);
3770     DrawText(mSX + 3 * 32, mSY + ypos * 32,
3771              (setup.input[player_nr].use_joystick ?
3772               custom[i].text :
3773               getKeyNameFromKey(*custom[i].key)), FONT_VALUE_1);
3774   }
3775 }
3776
3777 static int input_player_nr = 0;
3778
3779 void HandleSetupScreen_Input_Player(int step, int direction)
3780 {
3781   int old_player_nr = input_player_nr;
3782   int new_player_nr;
3783
3784   new_player_nr = old_player_nr + step * direction;
3785   if (new_player_nr < 0)
3786     new_player_nr = 0;
3787   if (new_player_nr > MAX_PLAYERS - 1)
3788     new_player_nr = MAX_PLAYERS - 1;
3789
3790   if (new_player_nr != old_player_nr)
3791   {
3792     input_player_nr = new_player_nr;
3793
3794     drawPlayerSetupInputInfo(input_player_nr, FALSE);
3795   }
3796 }
3797
3798 void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button)
3799 {
3800   static int choice = 0;
3801   int x = 0;
3802   int y = choice;
3803   int pos_start  = SETUPINPUT_SCREEN_POS_START;
3804   int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1;
3805   int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2;
3806   int pos_end    = SETUPINPUT_SCREEN_POS_END;
3807
3808   if (button == MB_MENU_INITIALIZE)
3809   {
3810     drawPlayerSetupInputInfo(input_player_nr, (choice == 2));
3811
3812 #if 1
3813     DrawCursorAndText_Setup(choice, TRUE);
3814 #else
3815     drawCursor(choice, TRUE);
3816 #endif
3817
3818     return;
3819   }
3820   else if (button == MB_MENU_LEAVE)
3821   {
3822     setup_mode = SETUP_MODE_MAIN;
3823     DrawSetupScreen();
3824     InitJoysticks();
3825
3826     return;
3827   }
3828
3829   if (mx || my)         /* mouse input */
3830   {
3831     x = (mx - mSX) / 32;
3832     y = (my - mSY) / 32 - MENU_SCREEN_START_YPOS;
3833   }
3834   else if (dx || dy)    /* keyboard input */
3835   {
3836     if (dx && choice == 0)
3837       x = (dx < 0 ? 10 : 12);
3838     else if ((dx && choice == 1) ||
3839              (dx == +1 && choice == 2) ||
3840              (dx == -1 && choice == pos_end))
3841       button = MB_MENU_CHOICE;
3842     else if (dy)
3843       y = choice + dy;
3844
3845     if (y >= pos_empty1 && y <= pos_empty2)
3846       y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1);
3847   }
3848
3849   if (y == 0 && dx != 0 && button)
3850   {
3851     HandleSetupScreen_Input_Player(1, dx < 0 ? -1 : +1);
3852   }
3853   else if (IN_VIS_FIELD(x, y) &&
3854            y >= pos_start && y <= pos_end &&
3855            !(y >= pos_empty1 && y <= pos_empty2))
3856   {
3857     if (button)
3858     {
3859       if (y != choice)
3860       {
3861 #if 1
3862         DrawCursorAndText_Setup(choice, FALSE);
3863         DrawCursorAndText_Setup(y, TRUE);
3864
3865         drawPlayerSetupInputInfo(input_player_nr, (y == 2));
3866 #else
3867         drawCursor(choice, FALSE);
3868         drawCursor(y, TRUE);
3869 #endif
3870
3871         choice = y;
3872       }
3873     }
3874     else
3875     {
3876       if (y == 1)
3877       {
3878         char *device_name = setup.input[input_player_nr].joy.device_name;
3879
3880         if (!setup.input[input_player_nr].use_joystick)
3881         {
3882           int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1);
3883
3884           setJoystickDeviceToNr(device_name, new_device_nr);
3885           setup.input[input_player_nr].use_joystick = TRUE;
3886         }
3887         else
3888         {
3889           int device_nr = getJoystickNrFromDeviceName(device_name);
3890           int new_device_nr = device_nr + (dx >= 0 ? +1 : -1);
3891
3892           if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS)
3893             setup.input[input_player_nr].use_joystick = FALSE;
3894           else
3895             setJoystickDeviceToNr(device_name, new_device_nr);
3896         }
3897
3898         drawPlayerSetupInputInfo(input_player_nr, FALSE);
3899       }
3900       else if (y == 2)
3901       {
3902         if (setup.input[input_player_nr].use_joystick)
3903         {
3904           InitJoysticks();
3905           CalibrateJoystick(input_player_nr);
3906         }
3907         else
3908           CustomizeKeyboard(input_player_nr);
3909       }
3910       else if (y == pos_end)
3911       {
3912         InitJoysticks();
3913
3914         setup_mode = SETUP_MODE_MAIN;
3915         DrawSetupScreen();
3916       }
3917     }
3918   }
3919 }
3920
3921 void CustomizeKeyboard(int player_nr)
3922 {
3923   int i;
3924   int step_nr;
3925   boolean finished = FALSE;
3926   static struct SetupKeyboardInfo custom_key;
3927   static struct
3928   {
3929     Key *key;
3930     char *text;
3931   } customize_step[] =
3932   {
3933     { &custom_key.left,  "Move Left"    },
3934     { &custom_key.right, "Move Right"   },
3935     { &custom_key.up,    "Move Up"      },
3936     { &custom_key.down,  "Move Down"    },
3937     { &custom_key.snap,  "Snap Field"   },
3938     { &custom_key.drop,  "Drop Element" }
3939   };
3940
3941   /* read existing key bindings from player setup */
3942   custom_key = setup.input[player_nr].key;
3943
3944   ClearWindow();
3945
3946   DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Keyboard Input");
3947
3948   BackToFront();
3949   InitAnimation();
3950
3951   step_nr = 0;
3952   DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
3953            customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
3954   DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
3955            "Key:", FONT_INPUT_1_ACTIVE);
3956   DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3957            getKeyNameFromKey(*customize_step[step_nr].key), FONT_VALUE_OLD);
3958
3959   while (!finished)
3960   {
3961     if (PendingEvent())         /* got event */
3962     {
3963       Event event;
3964
3965       NextEvent(&event);
3966
3967       switch(event.type)
3968       {
3969         case EVENT_KEYPRESS:
3970           {
3971             Key key = GetEventKey((KeyEvent *)&event, FALSE);
3972
3973             if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6))
3974             {
3975               finished = TRUE;
3976               break;
3977             }
3978
3979             /* all keys configured -- wait for "Escape" or "Return" key */
3980             if (step_nr == 6)
3981               break;
3982
3983             /* press 'Enter' to keep the existing key binding */
3984             if (key == KSYM_Return)
3985               key = *customize_step[step_nr].key;
3986
3987             /* check if key already used */
3988             for (i = 0; i < step_nr; i++)
3989               if (*customize_step[i].key == key)
3990                 break;
3991             if (i < step_nr)
3992               break;
3993
3994             /* got new key binding */
3995             *customize_step[step_nr].key = key;
3996             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3997                      "             ", FONT_VALUE_1);
3998             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
3999                      getKeyNameFromKey(key), FONT_VALUE_1);
4000             step_nr++;
4001
4002             /* un-highlight last query */
4003             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1)) * 32,
4004                      customize_step[step_nr - 1].text, FONT_MENU_1);
4005             DrawText(mSX, mSY + (2 + 2 * (step_nr - 1) + 1) * 32,
4006                      "Key:", FONT_MENU_1);
4007
4008             /* press 'Enter' to leave */
4009             if (step_nr == 6)
4010             {
4011               DrawText(mSX + 16, mSY + 15 * 32 + 16,
4012                        "Press Enter", FONT_TITLE_1);
4013               break;
4014             }
4015
4016             /* query next key binding */
4017             DrawText(mSX, mSY + (2 + 2 * step_nr) * 32,
4018                      customize_step[step_nr].text, FONT_INPUT_1_ACTIVE);
4019             DrawText(mSX, mSY + (2 + 2 * step_nr + 1) * 32,
4020                      "Key:", FONT_INPUT_1_ACTIVE);
4021             DrawText(mSX + 4 * 32, mSY + (2 + 2 * step_nr + 1) * 32,
4022                      getKeyNameFromKey(*customize_step[step_nr].key),
4023                      FONT_VALUE_OLD);
4024           }
4025           break;
4026
4027         case EVENT_KEYRELEASE:
4028           key_joystick_mapping = 0;
4029           break;
4030
4031         default:
4032           HandleOtherEvents(&event);
4033           break;
4034       }
4035     }
4036
4037     DoAnimation();
4038     BackToFront();
4039
4040     /* don't eat all CPU time */
4041     Delay(10);
4042   }
4043
4044   /* write new key bindings back to player setup */
4045   setup.input[player_nr].key = custom_key;
4046
4047   StopAnimation();
4048   DrawSetupScreen_Input();
4049 }
4050
4051 static boolean CalibrateJoystickMain(int player_nr)
4052 {
4053   int new_joystick_xleft = JOYSTICK_XMIDDLE;
4054   int new_joystick_xright = JOYSTICK_XMIDDLE;
4055   int new_joystick_yupper = JOYSTICK_YMIDDLE;
4056   int new_joystick_ylower = JOYSTICK_YMIDDLE;
4057   int new_joystick_xmiddle, new_joystick_ymiddle;
4058
4059   int joystick_fd = joystick.fd[player_nr];
4060   int x, y, last_x, last_y, xpos = 8, ypos = 3;
4061   boolean check[3][3];
4062   int check_remaining = 3 * 3;
4063   int joy_x, joy_y;
4064   int joy_value;
4065   int result = -1;
4066
4067   if (joystick.status == JOYSTICK_NOT_AVAILABLE)
4068     return FALSE;
4069
4070   if (joystick_fd < 0 || !setup.input[player_nr].use_joystick)
4071     return FALSE;
4072
4073   ClearWindow();
4074
4075   for (y = 0; y < 3; y++)
4076   {
4077     for (x = 0; x < 3; x++)
4078     {
4079       DrawGraphic(xpos + x - 1, ypos + y - 1, IMG_MENU_CALIBRATE_BLUE, 0);
4080       check[x][y] = FALSE;
4081     }
4082   }
4083
4084   DrawTextSCentered(mSY - SY +  6 * 32, FONT_TITLE_1, "Rotate joystick");
4085   DrawTextSCentered(mSY - SY +  7 * 32, FONT_TITLE_1, "in all directions");
4086   DrawTextSCentered(mSY - SY +  9 * 32, FONT_TITLE_1, "if all balls");
4087   DrawTextSCentered(mSY - SY + 10 * 32, FONT_TITLE_1, "are marked,");
4088   DrawTextSCentered(mSY - SY + 11 * 32, FONT_TITLE_1, "center joystick");
4089   DrawTextSCentered(mSY - SY + 12 * 32, FONT_TITLE_1, "and");
4090   DrawTextSCentered(mSY - SY + 13 * 32, FONT_TITLE_1, "press any button!");
4091
4092   joy_value = Joystick(player_nr);
4093   last_x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
4094   last_y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
4095
4096   /* eventually uncalibrated center position (joystick could be uncentered) */
4097   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
4098     return FALSE;
4099
4100   new_joystick_xmiddle = joy_x;
4101   new_joystick_ymiddle = joy_y;
4102
4103   DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_RED, 0);
4104   BackToFront();
4105
4106   while (Joystick(player_nr) & JOY_BUTTON);     /* wait for released button */
4107   InitAnimation();
4108
4109   while (result < 0)
4110   {
4111     if (PendingEvent())         /* got event */
4112     {
4113       Event event;
4114
4115       NextEvent(&event);
4116
4117       switch(event.type)
4118       {
4119         case EVENT_KEYPRESS:
4120           switch(GetEventKey((KeyEvent *)&event, TRUE))
4121           {
4122             case KSYM_Return:
4123               if (check_remaining == 0)
4124                 result = 1;
4125               break;
4126
4127             case KSYM_Escape:
4128               result = 0;
4129               break;
4130
4131             default:
4132               break;
4133           }
4134           break;
4135
4136         case EVENT_KEYRELEASE:
4137           key_joystick_mapping = 0;
4138           break;
4139
4140         default:
4141           HandleOtherEvents(&event);
4142           break;
4143       }
4144     }
4145
4146     if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
4147       return FALSE;
4148
4149     new_joystick_xleft  = MIN(new_joystick_xleft,  joy_x);
4150     new_joystick_xright = MAX(new_joystick_xright, joy_x);
4151     new_joystick_yupper = MIN(new_joystick_yupper, joy_y);
4152     new_joystick_ylower = MAX(new_joystick_ylower, joy_y);
4153
4154     setup.input[player_nr].joy.xleft = new_joystick_xleft;
4155     setup.input[player_nr].joy.yupper = new_joystick_yupper;
4156     setup.input[player_nr].joy.xright = new_joystick_xright;
4157     setup.input[player_nr].joy.ylower = new_joystick_ylower;
4158     setup.input[player_nr].joy.xmiddle = new_joystick_xmiddle;
4159     setup.input[player_nr].joy.ymiddle = new_joystick_ymiddle;
4160
4161     CheckJoystickData();
4162
4163     joy_value = Joystick(player_nr);
4164
4165     if (joy_value & JOY_BUTTON && check_remaining == 0)
4166       result = 1;
4167
4168     x = (joy_value & JOY_LEFT ? -1 : joy_value & JOY_RIGHT ? +1 : 0);
4169     y = (joy_value & JOY_UP   ? -1 : joy_value & JOY_DOWN  ? +1 : 0);
4170
4171     if (x != last_x || y != last_y)
4172     {
4173       DrawGraphic(xpos + last_x, ypos + last_y, IMG_MENU_CALIBRATE_YELLOW, 0);
4174       DrawGraphic(xpos + x,      ypos + y,      IMG_MENU_CALIBRATE_RED,    0);
4175
4176       last_x = x;
4177       last_y = y;
4178
4179       if (check_remaining > 0 && !check[x+1][y+1])
4180       {
4181         check[x+1][y+1] = TRUE;
4182         check_remaining--;
4183       }
4184
4185 #if 0
4186 #ifdef DEBUG
4187       printf("LEFT / MIDDLE / RIGHT == %d / %d / %d\n",
4188              setup.input[player_nr].joy.xleft,
4189              setup.input[player_nr].joy.xmiddle,
4190              setup.input[player_nr].joy.xright);
4191       printf("UP / MIDDLE / DOWN == %d / %d / %d\n",
4192              setup.input[player_nr].joy.yupper,
4193              setup.input[player_nr].joy.ymiddle,
4194              setup.input[player_nr].joy.ylower);
4195 #endif
4196 #endif
4197
4198     }
4199
4200     DoAnimation();
4201     BackToFront();
4202
4203     /* don't eat all CPU time */
4204     Delay(10);
4205   }
4206
4207   /* calibrated center position (joystick should now be centered) */
4208   if (!ReadJoystick(joystick_fd, &joy_x, &joy_y, NULL, NULL))
4209     return FALSE;
4210
4211   new_joystick_xmiddle = joy_x;
4212   new_joystick_ymiddle = joy_y;
4213
4214   StopAnimation();
4215
4216 #if 0
4217   DrawSetupScreen_Input();
4218 #endif
4219
4220   /* wait until the last pressed button was released */
4221   while (Joystick(player_nr) & JOY_BUTTON)
4222   {
4223     if (PendingEvent())         /* got event */
4224     {
4225       Event event;
4226
4227       NextEvent(&event);
4228       HandleOtherEvents(&event);
4229
4230       Delay(10);
4231     }
4232   }
4233
4234   return TRUE;
4235 }
4236
4237 void CalibrateJoystick(int player_nr)
4238 {
4239   if (!CalibrateJoystickMain(player_nr))
4240   {
4241     char *device_name = setup.input[player_nr].joy.device_name;
4242     int nr = getJoystickNrFromDeviceName(device_name) + 1;
4243     int xpos = mSX - SX;
4244     int ypos = mSY - SY;
4245
4246     ClearWindow();
4247
4248     DrawTextF(xpos + 16, ypos + 6 * 32, FONT_TITLE_1, "   JOYSTICK %d   ", nr);
4249     DrawTextF(xpos + 16, ypos + 7 * 32, FONT_TITLE_1, " NOT AVAILABLE! ");
4250     BackToFront();
4251
4252     Delay(2000);                /* show error message for a short time */
4253
4254     ClearEventQueue();
4255   }
4256
4257 #if 1
4258   DrawSetupScreen_Input();
4259 #endif
4260 }
4261
4262 void DrawSetupScreen()
4263 {
4264   DeactivateJoystick();
4265
4266   SetMainBackgroundImage(IMG_BACKGROUND_SETUP);
4267
4268   if (setup_mode == SETUP_MODE_INPUT)
4269     DrawSetupScreen_Input();
4270   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
4271     DrawChooseTree(&screen_mode_current);
4272   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
4273     DrawChooseTree(&artwork.gfx_current);
4274   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
4275     DrawChooseTree(&artwork.snd_current);
4276   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
4277     DrawChooseTree(&artwork.mus_current);
4278   else
4279     DrawSetupScreen_Generic();
4280
4281   PlayMenuSound();
4282   PlayMenuMusic();
4283 }
4284
4285 void RedrawSetupScreenAfterFullscreenToggle()
4286 {
4287   if (setup_mode == SETUP_MODE_GRAPHICS)
4288     DrawSetupScreen();
4289 }
4290
4291 void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
4292 {
4293   if (setup_mode == SETUP_MODE_INPUT)
4294     HandleSetupScreen_Input(mx, my, dx, dy, button);
4295   else if (setup_mode == SETUP_MODE_CHOOSE_SCREEN_MODE)
4296     HandleChooseTree(mx, my, dx, dy, button, &screen_mode_current);
4297   else if (setup_mode == SETUP_MODE_CHOOSE_GRAPHICS)
4298     HandleChooseTree(mx, my, dx, dy, button, &artwork.gfx_current);
4299   else if (setup_mode == SETUP_MODE_CHOOSE_SOUNDS)
4300     HandleChooseTree(mx, my, dx, dy, button, &artwork.snd_current);
4301   else if (setup_mode == SETUP_MODE_CHOOSE_MUSIC)
4302     HandleChooseTree(mx, my, dx, dy, button, &artwork.mus_current);
4303   else
4304     HandleSetupScreen_Generic(mx, my, dx, dy, button);
4305
4306   DoAnimation();
4307 }
4308
4309 void HandleGameActions()
4310 {
4311   if (game_status != GAME_MODE_PLAYING)
4312     return;
4313
4314   GameActions();        /* main game loop */
4315
4316   if (tape.auto_play && !tape.playing)
4317     AutoPlayTape();     /* continue automatically playing next tape */
4318 }
4319
4320
4321 /* ---------- new screen button stuff -------------------------------------- */
4322
4323 static void getScreenMenuButtonPos(int *x, int *y, int gadget_id)
4324 {
4325   switch (gadget_id)
4326   {
4327 #if 1
4328     case SCREEN_CTRL_ID_PREV_LEVEL:
4329       *x = mSX + menu.main.button.prev_level.x;
4330       *y = mSY + menu.main.button.prev_level.y;
4331       break;
4332
4333     case SCREEN_CTRL_ID_NEXT_LEVEL:
4334       *x = mSX + menu.main.button.next_level.x;
4335       *y = mSY + menu.main.button.next_level.y;
4336       break;
4337 #else
4338     case SCREEN_CTRL_ID_PREV_LEVEL:
4339       *x = mSX + TILEX * getPrevlevelButtonPos();
4340       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
4341       break;
4342
4343     case SCREEN_CTRL_ID_NEXT_LEVEL:
4344       *x = mSX + TILEX * getNextLevelButtonPos();
4345       *y = mSY + TILEY * (MENU_SCREEN_START_YPOS + 1);
4346       break;
4347 #endif
4348
4349     case SCREEN_CTRL_ID_PREV_PLAYER:
4350       *x = mSX + TILEX * 10;
4351       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
4352       break;
4353
4354     case SCREEN_CTRL_ID_NEXT_PLAYER:
4355       *x = mSX + TILEX * 12;
4356       *y = mSY + TILEY * MENU_SCREEN_START_YPOS;
4357       break;
4358
4359     default:
4360       Error(ERR_EXIT, "unknown gadget ID %d", gadget_id);
4361   }
4362 }
4363
4364 static struct
4365 {
4366   int gfx_unpressed, gfx_pressed;
4367   void (*get_gadget_position)(int *, int *, int);
4368   int gadget_id;
4369   int screen_mask;
4370   char *infotext;
4371 } menubutton_info[NUM_SCREEN_MENUBUTTONS] =
4372 {
4373   {
4374     IMG_MENU_BUTTON_PREV_LEVEL, IMG_MENU_BUTTON_PREV_LEVEL_ACTIVE,
4375     getScreenMenuButtonPos,
4376     SCREEN_CTRL_ID_PREV_LEVEL,
4377     SCREEN_MASK_MAIN,
4378     "last level"
4379   },
4380   {
4381     IMG_MENU_BUTTON_NEXT_LEVEL, IMG_MENU_BUTTON_NEXT_LEVEL_ACTIVE,
4382     getScreenMenuButtonPos,
4383     SCREEN_CTRL_ID_NEXT_LEVEL,
4384     SCREEN_MASK_MAIN,
4385     "next level"
4386   },
4387   {
4388     IMG_MENU_BUTTON_LEFT, IMG_MENU_BUTTON_LEFT_ACTIVE,
4389     getScreenMenuButtonPos,
4390     SCREEN_CTRL_ID_PREV_PLAYER,
4391     SCREEN_MASK_INPUT,
4392     "last player"
4393   },
4394   {
4395     IMG_MENU_BUTTON_RIGHT, IMG_MENU_BUTTON_RIGHT_ACTIVE,
4396     getScreenMenuButtonPos,
4397     SCREEN_CTRL_ID_NEXT_PLAYER,
4398     SCREEN_MASK_INPUT,
4399     "next player"
4400   },
4401 };
4402
4403 static struct
4404 {
4405   int gfx_unpressed, gfx_pressed;
4406   int x, y;
4407   int gadget_id;
4408   char *infotext;
4409 } scrollbutton_info[NUM_SCREEN_SCROLLBUTTONS] =
4410 {
4411   {
4412     IMG_MENU_BUTTON_UP, IMG_MENU_BUTTON_UP_ACTIVE,
4413     SC_SCROLL_UP_XPOS, SC_SCROLL_UP_YPOS,
4414     SCREEN_CTRL_ID_SCROLL_UP,
4415     "scroll up"
4416   },
4417   {
4418     IMG_MENU_BUTTON_DOWN, IMG_MENU_BUTTON_DOWN_ACTIVE,
4419     SC_SCROLL_DOWN_XPOS, SC_SCROLL_DOWN_YPOS,
4420     SCREEN_CTRL_ID_SCROLL_DOWN,
4421     "scroll down"
4422   }
4423 };
4424
4425 static struct
4426 {
4427 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4428   Bitmap **gfx_unpressed, **gfx_pressed;
4429 #else
4430   int gfx_unpressed, gfx_pressed;
4431 #endif
4432   int x, y;
4433   int width, height;
4434   int type;
4435   int gadget_id;
4436   char *infotext;
4437 } scrollbar_info[NUM_SCREEN_SCROLLBARS] =
4438 {
4439   {
4440 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4441     &scrollbar_bitmap[0], &scrollbar_bitmap[1],
4442 #else
4443     IMG_MENU_SCROLLBAR, IMG_MENU_SCROLLBAR_ACTIVE,
4444 #endif
4445     SC_SCROLL_VERTICAL_XPOS, SC_SCROLL_VERTICAL_YPOS,
4446     SC_SCROLL_VERTICAL_XSIZE, SC_SCROLL_VERTICAL_YSIZE,
4447     GD_TYPE_SCROLLBAR_VERTICAL,
4448     SCREEN_CTRL_ID_SCROLL_VERTICAL,
4449     "scroll level series vertically"
4450   }
4451 };
4452
4453 static void CreateScreenMenubuttons()
4454 {
4455   struct GadgetInfo *gi;
4456   unsigned long event_mask;
4457   int i;
4458
4459   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
4460   {
4461     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4462     int gfx_unpressed, gfx_pressed;
4463     int x, y, width, height;
4464     int gd_x1, gd_x2, gd_y1, gd_y2;
4465     int id = menubutton_info[i].gadget_id;
4466
4467     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
4468
4469     menubutton_info[i].get_gadget_position(&x, &y, id);
4470
4471     width = SC_MENUBUTTON_XSIZE;
4472     height = SC_MENUBUTTON_YSIZE;
4473
4474     gfx_unpressed = menubutton_info[i].gfx_unpressed;
4475     gfx_pressed   = menubutton_info[i].gfx_pressed;
4476     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4477     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4478     gd_x1 = graphic_info[gfx_unpressed].src_x;
4479     gd_y1 = graphic_info[gfx_unpressed].src_y;
4480     gd_x2 = graphic_info[gfx_pressed].src_x;
4481     gd_y2 = graphic_info[gfx_pressed].src_y;
4482
4483     gi = CreateGadget(GDI_CUSTOM_ID, id,
4484                       GDI_CUSTOM_TYPE_ID, i,
4485                       GDI_INFO_TEXT, menubutton_info[i].infotext,
4486                       GDI_X, x,
4487                       GDI_Y, y,
4488                       GDI_WIDTH, width,
4489                       GDI_HEIGHT, height,
4490                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4491                       GDI_STATE, GD_BUTTON_UNPRESSED,
4492                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4493                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4494                       GDI_DIRECT_DRAW, FALSE,
4495                       GDI_EVENT_MASK, event_mask,
4496                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4497                       GDI_END);
4498
4499     if (gi == NULL)
4500       Error(ERR_EXIT, "cannot create gadget");
4501
4502     screen_gadget[id] = gi;
4503   }
4504 }
4505
4506 static void CreateScreenScrollbuttons()
4507 {
4508   struct GadgetInfo *gi;
4509   unsigned long event_mask;
4510   int i;
4511
4512   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
4513   {
4514     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4515     int gfx_unpressed, gfx_pressed;
4516     int x, y, width, height;
4517     int gd_x1, gd_x2, gd_y1, gd_y2;
4518     int id = scrollbutton_info[i].gadget_id;
4519
4520     event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
4521
4522     x = mSX + scrollbutton_info[i].x + menu.scrollbar_xoffset;
4523     y = mSY + scrollbutton_info[i].y;
4524     width = SC_SCROLLBUTTON_XSIZE;
4525     height = SC_SCROLLBUTTON_YSIZE;
4526
4527     if (id == SCREEN_CTRL_ID_SCROLL_DOWN)
4528       y = mSY + (SC_SCROLL_VERTICAL_YPOS +
4529                  (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE);
4530
4531     gfx_unpressed = scrollbutton_info[i].gfx_unpressed;
4532     gfx_pressed   = scrollbutton_info[i].gfx_pressed;
4533     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4534     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4535     gd_x1 = graphic_info[gfx_unpressed].src_x;
4536     gd_y1 = graphic_info[gfx_unpressed].src_y;
4537     gd_x2 = graphic_info[gfx_pressed].src_x;
4538     gd_y2 = graphic_info[gfx_pressed].src_y;
4539
4540     gi = CreateGadget(GDI_CUSTOM_ID, id,
4541                       GDI_CUSTOM_TYPE_ID, i,
4542                       GDI_INFO_TEXT, scrollbutton_info[i].infotext,
4543                       GDI_X, x,
4544                       GDI_Y, y,
4545                       GDI_WIDTH, width,
4546                       GDI_HEIGHT, height,
4547                       GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4548                       GDI_STATE, GD_BUTTON_UNPRESSED,
4549                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4550                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4551                       GDI_DIRECT_DRAW, FALSE,
4552                       GDI_EVENT_MASK, event_mask,
4553                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4554                       GDI_END);
4555
4556     if (gi == NULL)
4557       Error(ERR_EXIT, "cannot create gadget");
4558
4559     screen_gadget[id] = gi;
4560   }
4561 }
4562
4563 static void CreateScreenScrollbars()
4564 {
4565   int i;
4566
4567   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
4568   {
4569     Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed;
4570 #if !defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4571     int gfx_unpressed, gfx_pressed;
4572 #endif
4573     int x, y, width, height;
4574     int gd_x1, gd_x2, gd_y1, gd_y2;
4575     struct GadgetInfo *gi;
4576     int items_max, items_visible, item_position;
4577     unsigned long event_mask;
4578     int num_page_entries = NUM_MENU_ENTRIES_ON_SCREEN;
4579     int id = scrollbar_info[i].gadget_id;
4580
4581     event_mask = GD_EVENT_MOVING | GD_EVENT_OFF_BORDERS;
4582
4583     x = mSX + scrollbar_info[i].x + menu.scrollbar_xoffset;
4584     y = mSY + scrollbar_info[i].y;
4585     width  = scrollbar_info[i].width;
4586     height = scrollbar_info[i].height;
4587
4588     if (id == SCREEN_CTRL_ID_SCROLL_VERTICAL)
4589       height = (NUM_MENU_ENTRIES_ON_SCREEN - 2) * SC_SCROLLBUTTON_YSIZE;
4590
4591     items_max = num_page_entries;
4592     items_visible = num_page_entries;
4593     item_position = 0;
4594
4595 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4596     gd_bitmap_unpressed = *scrollbar_info[i].gfx_unpressed;
4597     gd_bitmap_pressed   = *scrollbar_info[i].gfx_pressed;
4598     gd_x1 = 0;
4599     gd_y1 = 0;
4600     gd_x2 = 0;
4601     gd_y2 = 0;
4602 #else
4603     gfx_unpressed = scrollbar_info[i].gfx_unpressed;
4604     gfx_pressed   = scrollbar_info[i].gfx_pressed;
4605     gd_bitmap_unpressed = graphic_info[gfx_unpressed].bitmap;
4606     gd_bitmap_pressed   = graphic_info[gfx_pressed].bitmap;
4607     gd_x1 = graphic_info[gfx_unpressed].src_x;
4608     gd_y1 = graphic_info[gfx_unpressed].src_y;
4609     gd_x2 = graphic_info[gfx_pressed].src_x;
4610     gd_y2 = graphic_info[gfx_pressed].src_y;
4611 #endif
4612
4613     gi = CreateGadget(GDI_CUSTOM_ID, id,
4614                       GDI_CUSTOM_TYPE_ID, i,
4615                       GDI_INFO_TEXT, scrollbar_info[i].infotext,
4616                       GDI_X, x,
4617                       GDI_Y, y,
4618                       GDI_WIDTH, width,
4619                       GDI_HEIGHT, height,
4620                       GDI_TYPE, scrollbar_info[i].type,
4621                       GDI_SCROLLBAR_ITEMS_MAX, items_max,
4622                       GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
4623                       GDI_SCROLLBAR_ITEM_POSITION, item_position,
4624 #if 1
4625                       GDI_WHEEL_AREA_X, SX,
4626                       GDI_WHEEL_AREA_Y, SY,
4627                       GDI_WHEEL_AREA_WIDTH, SXSIZE,
4628                       GDI_WHEEL_AREA_HEIGHT, SYSIZE,
4629 #else
4630                       GDI_WHEEL_AREA_X, 0,
4631                       GDI_WHEEL_AREA_Y, 0,
4632                       GDI_WHEEL_AREA_WIDTH, WIN_XSIZE,
4633                       GDI_WHEEL_AREA_HEIGHT, WIN_YSIZE,
4634 #endif
4635                       GDI_STATE, GD_BUTTON_UNPRESSED,
4636                       GDI_DESIGN_UNPRESSED, gd_bitmap_unpressed, gd_x1, gd_y1,
4637                       GDI_DESIGN_PRESSED, gd_bitmap_pressed, gd_x2, gd_y2,
4638                       GDI_BORDER_SIZE, SC_BORDER_SIZE, SC_BORDER_SIZE,
4639                       GDI_DIRECT_DRAW, FALSE,
4640                       GDI_EVENT_MASK, event_mask,
4641                       GDI_CALLBACK_ACTION, HandleScreenGadgets,
4642                       GDI_END);
4643
4644     if (gi == NULL)
4645       Error(ERR_EXIT, "cannot create gadget");
4646
4647     screen_gadget[id] = gi;
4648   }
4649 }
4650
4651 void CreateScreenGadgets()
4652 {
4653   int last_game_status = game_status;   /* save current game status */
4654
4655 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4656   int i;
4657
4658   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
4659   {
4660     scrollbar_bitmap[i] = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
4661
4662     /* copy pointers to clip mask and GC */
4663     scrollbar_bitmap[i]->clip_mask =
4664       graphic_info[IMG_MENU_SCROLLBAR + i].clip_mask;
4665     scrollbar_bitmap[i]->stored_clip_gc =
4666       graphic_info[IMG_MENU_SCROLLBAR + i].clip_gc;
4667
4668     BlitBitmap(graphic_info[IMG_MENU_SCROLLBAR + i].bitmap,
4669                scrollbar_bitmap[i],
4670                graphic_info[IMG_MENU_SCROLLBAR + i].src_x,
4671                graphic_info[IMG_MENU_SCROLLBAR + i].src_y,
4672                TILEX, TILEY, 0, 0);
4673   }
4674 #endif
4675
4676   CreateScreenMenubuttons();
4677
4678   /* force LEVELS draw offset for scrollbar / scrollbutton gadgets */
4679   game_status = GAME_MODE_LEVELS;
4680
4681   CreateScreenScrollbuttons();
4682   CreateScreenScrollbars();
4683
4684   game_status = last_game_status;       /* restore current game status */
4685 }
4686
4687 void FreeScreenGadgets()
4688 {
4689   int i;
4690
4691 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
4692   for (i = 0; i < NUM_SCROLLBAR_BITMAPS; i++)
4693   {
4694     /* prevent freeing clip mask and GC twice */
4695     scrollbar_bitmap[i]->clip_mask = None;
4696     scrollbar_bitmap[i]->stored_clip_gc = None;
4697
4698     FreeBitmap(scrollbar_bitmap[i]);
4699   }
4700 #endif
4701
4702   for (i = 0; i < NUM_SCREEN_GADGETS; i++)
4703     FreeGadget(screen_gadget[i]);
4704 }
4705
4706 void MapScreenMenuGadgets(int screen_mask)
4707 {
4708   int i;
4709
4710   for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++)
4711     if (screen_mask & menubutton_info[i].screen_mask)
4712       MapGadget(screen_gadget[menubutton_info[i].gadget_id]);
4713 }
4714
4715 void MapScreenTreeGadgets(TreeInfo *ti)
4716 {
4717   int num_entries = numTreeInfoInGroup(ti);
4718   int i;
4719
4720   if (num_entries <= NUM_MENU_ENTRIES_ON_SCREEN)
4721     return;
4722
4723   for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++)
4724     MapGadget(screen_gadget[scrollbutton_info[i].gadget_id]);
4725
4726   for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++)
4727     MapGadget(screen_gadget[scrollbar_info[i].gadget_id]);
4728 }
4729
4730 static void HandleScreenGadgets(struct GadgetInfo *gi)
4731 {
4732   int id = gi->custom_id;
4733   int button = gi->event.button;
4734   int step = (button == 1 ? 1 : button == 2 ? 5 : 10);
4735
4736   switch (id)
4737   {
4738     case SCREEN_CTRL_ID_PREV_LEVEL:
4739       HandleMainMenu_SelectLevel(step, -1);
4740       break;
4741
4742     case SCREEN_CTRL_ID_NEXT_LEVEL:
4743       HandleMainMenu_SelectLevel(step, +1);
4744       break;
4745
4746     case SCREEN_CTRL_ID_PREV_PLAYER:
4747       HandleSetupScreen_Input_Player(step, -1);
4748       break;
4749
4750     case SCREEN_CTRL_ID_NEXT_PLAYER:
4751       HandleSetupScreen_Input_Player(step, +1);
4752       break;
4753
4754     case SCREEN_CTRL_ID_SCROLL_UP:
4755       if (game_status == GAME_MODE_LEVELS)
4756         HandleChooseLevel(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
4757       else if (game_status == GAME_MODE_SETUP)
4758         HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK);
4759       break;
4760
4761     case SCREEN_CTRL_ID_SCROLL_DOWN:
4762       if (game_status == GAME_MODE_LEVELS)
4763         HandleChooseLevel(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4764       else if (game_status == GAME_MODE_SETUP)
4765         HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK);
4766       break;
4767
4768     case SCREEN_CTRL_ID_SCROLL_VERTICAL:
4769       if (game_status == GAME_MODE_LEVELS)
4770         HandleChooseLevel(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4771       else if (game_status == GAME_MODE_SETUP)
4772         HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
4773       break;
4774
4775     default:
4776       break;
4777   }
4778 }