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