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