X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fscreens.c;h=1233b803dbe8b7b57c664c596acf0509fde28a72;hb=0628b5c9ca7d213724d90b7603c54692fc7e7dc4;hp=61e308524a0766bde84c654f7ee6e3c1ea902f8e;hpb=234748b5eb0bc4fe29a36fd25a1d25658f53858a;p=rocksndiamonds.git diff --git a/src/screens.c b/src/screens.c index 61e30852..1233b803 100644 --- a/src/screens.c +++ b/src/screens.c @@ -1,15 +1,13 @@ -/*********************************************************** -* Rocks'n'Diamonds -- McDuffin Strikes Back! * -*----------------------------------------------------------* -* (c) 1995-2002 Artsoft Entertainment * -* Holger Schemel * -* Detmolder Strasse 189 * -* 33604 Bielefeld * -* Germany * -* e-mail: info@artsoft.org * -*----------------------------------------------------------* -* screens.c * -***********************************************************/ +// ============================================================================ +// Rocks'n'Diamonds - McDuffin Strikes Back! +// ---------------------------------------------------------------------------- +// (c) 1995-2014 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// https://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// screens.c +// ============================================================================ #include "libgame/libgame.h" @@ -20,2647 +18,10634 @@ #include "editor.h" #include "files.h" #include "tape.h" -#include "cartoons.h" +#include "anim.h" #include "network.h" #include "init.h" +#include "config.h" +#include "api.h" -/* screens in the setup menu */ + +#define DEBUG_JOYSTICKS 0 + + +// screens on the info screen +#define INFO_MODE_MAIN 0 +#define INFO_MODE_TITLE 1 +#define INFO_MODE_ELEMENTS 2 +#define INFO_MODE_MUSIC 3 +#define INFO_MODE_CREDITS 4 +#define INFO_MODE_PROGRAM 5 +#define INFO_MODE_VERSION 6 +#define INFO_MODE_LEVELSET 7 + +#define MAX_INFO_MODES 8 + +// screens on the setup screen +// (must match GFX_SPECIAL_ARG_SETUP_* values as defined in src/main.h) +// (should also match corresponding entries in src/conf_gfx.c) #define SETUP_MODE_MAIN 0 #define SETUP_MODE_GAME 1 -#define SETUP_MODE_INPUT 2 -#define SETUP_MODE_SHORTCUT 3 +#define SETUP_MODE_ENGINES 2 +#define SETUP_MODE_EDITOR 3 #define SETUP_MODE_GRAPHICS 4 #define SETUP_MODE_SOUND 5 #define SETUP_MODE_ARTWORK 6 -#define SETUP_MODE_CHOOSE_GRAPHICS 7 -#define SETUP_MODE_CHOOSE_SOUNDS 8 -#define SETUP_MODE_CHOOSE_MUSIC 9 +#define SETUP_MODE_INPUT 7 +#define SETUP_MODE_TOUCH 8 +#define SETUP_MODE_SHORTCUTS 9 +#define SETUP_MODE_SHORTCUTS_1 10 +#define SETUP_MODE_SHORTCUTS_2 11 +#define SETUP_MODE_SHORTCUTS_3 12 +#define SETUP_MODE_SHORTCUTS_4 13 +#define SETUP_MODE_SHORTCUTS_5 14 + +// sub-screens on the setup screen (generic) +#define SETUP_MODE_CHOOSE_ARTWORK 15 +#define SETUP_MODE_CHOOSE_OTHER 16 + +// sub-screens on the setup screen (specific) +#define SETUP_MODE_CHOOSE_SCORES_TYPE 17 +#define SETUP_MODE_CHOOSE_GAME_SPEED 18 +#define SETUP_MODE_CHOOSE_SCROLL_DELAY 19 +#define SETUP_MODE_CHOOSE_SNAPSHOT_MODE 20 +#define SETUP_MODE_CHOOSE_WINDOW_SIZE 21 +#define SETUP_MODE_CHOOSE_SCALING_TYPE 22 +#define SETUP_MODE_CHOOSE_RENDERING 23 +#define SETUP_MODE_CHOOSE_VSYNC 24 +#define SETUP_MODE_CHOOSE_GRAPHICS 25 +#define SETUP_MODE_CHOOSE_SOUNDS 26 +#define SETUP_MODE_CHOOSE_MUSIC 27 +#define SETUP_MODE_CHOOSE_VOLUME_SIMPLE 28 +#define SETUP_MODE_CHOOSE_VOLUME_LOOPS 29 +#define SETUP_MODE_CHOOSE_VOLUME_MUSIC 30 +#define SETUP_MODE_CHOOSE_TOUCH_CONTROL 31 +#define SETUP_MODE_CHOOSE_MOVE_DISTANCE 32 +#define SETUP_MODE_CHOOSE_DROP_DISTANCE 33 +#define SETUP_MODE_CHOOSE_TRANSPARENCY 34 +#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 35 +#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 36 +#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 37 +#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 38 +#define SETUP_MODE_CONFIG_VIRT_BUTTONS 39 + +#define MAX_SETUP_MODES 40 + +#define MAX_MENU_MODES MAX(MAX_INFO_MODES, MAX_SETUP_MODES) + +// info screen titles +#define STR_INFO_MAIN "Info Screen" +#define STR_INFO_TITLE "Title Screen" +#define STR_INFO_ELEMENTS "Game Elements" +#define STR_INFO_MUSIC "Music Info" +#define STR_INFO_CREDITS "Credits" +#define STR_INFO_PROGRAM "Program Info" +#define STR_INFO_VERSION "Version Info" +#define STR_INFO_LEVELSET "Level Set Info" +#define STR_INFO_EXIT "Exit" + +// setup screen titles +#define STR_SETUP_MAIN "Setup" +#define STR_SETUP_GAME "Game & Menu" +#define STR_SETUP_ENGINES "Game Engines" +#define STR_SETUP_EDITOR "Editor" +#define STR_SETUP_GRAPHICS "Graphics" +#define STR_SETUP_SOUND "Sound & Music" +#define STR_SETUP_ARTWORK "Custom Artwork" +#define STR_SETUP_INPUT "Input Devices" +#define STR_SETUP_TOUCH "Touch Controls" +#define STR_SETUP_SHORTCUTS "Key Shortcuts" +#define STR_SETUP_EXIT "Exit" +#define STR_SETUP_SAVE_AND_EXIT "Save and Exit" + +#define STR_SETUP_CHOOSE_SCORES_TYPE "Scores Type" +#define STR_SETUP_CHOOSE_GAME_SPEED "Game Speed" +#define STR_SETUP_CHOOSE_SCROLL_DELAY "Scroll Delay" +#define STR_SETUP_CHOOSE_SNAPSHOT_MODE "Snapshot Mode" +#define STR_SETUP_CHOOSE_WINDOW_SIZE "Window Scaling" +#define STR_SETUP_CHOOSE_SCALING_TYPE "Anti-Aliasing" +#define STR_SETUP_CHOOSE_RENDERING "Rendering Mode" +#define STR_SETUP_CHOOSE_VSYNC "VSync Mode" +#define STR_SETUP_CHOOSE_VOLUME_SIMPLE "Sound Volume" +#define STR_SETUP_CHOOSE_VOLUME_LOOPS "Loops Volume" +#define STR_SETUP_CHOOSE_VOLUME_MUSIC "Music Volume" +#define STR_SETUP_CHOOSE_TOUCH_CONTROL "Control Type" +#define STR_SETUP_CHOOSE_MOVE_DISTANCE "Move Distance" +#define STR_SETUP_CHOOSE_DROP_DISTANCE "Drop Distance" +#define STR_SETUP_CHOOSE_TRANSPARENCY "Transparency" +#define STR_SETUP_CHOOSE_GRID_XSIZE_0 "Horiz. Buttons" +#define STR_SETUP_CHOOSE_GRID_YSIZE_0 "Vert. Buttons" +#define STR_SETUP_CHOOSE_GRID_XSIZE_1 "Horiz. Buttons" +#define STR_SETUP_CHOOSE_GRID_YSIZE_1 "Vert. Buttons" + +// other screen text constants +#define STR_CHOOSE_TREE_EDIT "Edit" +#define MENU_CHOOSE_TREE_FONT(x) (FONT_TEXT_1 + (x)) +#define MENU_CHOOSE_TREE_COLOR(ti, a) TREE_COLOR(ti, a) + +#define TEXT_MAIN_MENU "Press any key or button for main menu" +#define TEXT_INFO_MENU "Press any key or button for info menu" +#define TEXT_NEXT_PAGE "Press any key or button for next page" +#define TEXT_NEXT_MENU (info_screens_from_main ? \ + TEXT_MAIN_MENU : TEXT_INFO_MENU) + +// for input setup functions +#define SETUPINPUT_SCREEN_POS_START 0 +#define SETUPINPUT_SCREEN_POS_EMPTY1 3 +#define SETUPINPUT_SCREEN_POS_EMPTY2 12 +#define SETUPINPUT_SCREEN_POS_END 13 -#define MAX_SETUP_MODES 10 +#define MENU_SETUP_FONT_TITLE FONT_TEXT_1 +#define MENU_SETUP_FONT_TEXT FONT_TITLE_2 -/* for input setup functions */ -#define SETUPINPUT_SCREEN_POS_START 0 -#define SETUPINPUT_SCREEN_POS_END (SCR_FIELDY - 4) -#define SETUPINPUT_SCREEN_POS_EMPTY1 (SETUPINPUT_SCREEN_POS_START + 3) -#define SETUPINPUT_SCREEN_POS_EMPTY2 (SETUPINPUT_SCREEN_POS_END - 1) +#define MAX_SETUP_TEXT_INPUT_LEN 28 -/* for various menu stuff */ -#define MAX_MENU_ENTRIES_ON_SCREEN (SCR_FIELDY - 2) +// for various menu stuff +#define MENU_SCREEN_START_XPOS 1 #define MENU_SCREEN_START_YPOS 2 -#define MENU_SCREEN_VALUE_XPOS 14 - -/* buttons and scrollbars identifiers */ -#define SCREEN_CTRL_ID_SCROLL_UP 0 -#define SCREEN_CTRL_ID_SCROLL_DOWN 1 -#define SCREEN_CTRL_ID_SCROLL_VERTICAL 2 +#define MENU_SCREEN_VALUE_XPOS (SCR_FIELDX - 3) +#define MENU_SCREEN_TEXT2_XPOS (SCR_FIELDX - 2) +#define MENU_SCREEN_MAX_XPOS (SCR_FIELDX - 1) +#define MENU_TITLE1_YPOS 8 +#define MENU_TITLE2_YPOS 46 +#define MENU_INFO_FONT_TITLE FONT_TEXT_1 +#define MENU_INFO_FONT_HEAD FONT_TEXT_2 +#define MENU_INFO_FONT_TEXT FONT_TEXT_3 +#define MENU_INFO_FONT_FOOT FONT_TEXT_4 +#define MENU_INFO_SPACE_HEAD (menu.headline2_spacing_info[info_mode]) +#define MENU_SCREEN_INFO_SPACE_LEFT (menu.left_spacing_info[info_mode]) +#define MENU_SCREEN_INFO_SPACE_RIGHT (menu.right_spacing_info[info_mode]) +#define MENU_SCREEN_INFO_SPACE_TOP (menu.top_spacing_info[info_mode]) +#define MENU_SCREEN_INFO_SPACE_BOTTOM (menu.bottom_spacing_info[info_mode]) +#define MENU_SCREEN_INFO_YSTART MENU_SCREEN_INFO_SPACE_TOP +#define MENU_SCREEN_INFO_YSTEP (TILEY + 4) +#define MENU_SCREEN_INFO_YBOTTOM (SYSIZE - MENU_SCREEN_INFO_SPACE_BOTTOM) +#define MENU_SCREEN_INFO_YSIZE (MENU_SCREEN_INFO_YBOTTOM - \ + MENU_SCREEN_INFO_YSTART - \ + TILEY / 2) +#define MAX_INFO_ELEMENTS_IN_ARRAY 128 +#define MAX_INFO_ELEMENTS_ON_SCREEN (SYSIZE / TILEY) +#define MAX_INFO_ELEMENTS MIN(MAX_INFO_ELEMENTS_IN_ARRAY, \ + MAX_INFO_ELEMENTS_ON_SCREEN) +#define STD_INFO_ELEMENTS_ON_SCREEN 10 +#define DYN_INFO_ELEMENTS_ON_SCREEN (MENU_SCREEN_INFO_YSIZE / \ + MENU_SCREEN_INFO_YSTEP) +#define DEFAULT_INFO_ELEMENTS MIN(STD_INFO_ELEMENTS_ON_SCREEN,\ + DYN_INFO_ELEMENTS_ON_SCREEN) +#define NUM_INFO_ELEMENTS_FROM_CONF \ + (menu.list_size_info[GFX_SPECIAL_ARG_INFO_ELEMENTS] > 0 ? \ + menu.list_size_info[GFX_SPECIAL_ARG_INFO_ELEMENTS] : \ + DEFAULT_INFO_ELEMENTS) +#define NUM_INFO_ELEMENTS_ON_SCREEN MIN(NUM_INFO_ELEMENTS_FROM_CONF,\ + MAX_INFO_ELEMENTS) +#define MAX_MENU_ENTRIES_ON_SCREEN (SCR_FIELDY - MENU_SCREEN_START_YPOS) +#define MAX_MENU_TEXT_LENGTH_BIG 13 +#define MAX_MENU_TEXT_LENGTH_MEDIUM (MAX_MENU_TEXT_LENGTH_BIG * 2) + +// screen gadget identifiers +#define SCREEN_CTRL_ID_PREV_LEVEL 0 +#define SCREEN_CTRL_ID_NEXT_LEVEL 1 +#define SCREEN_CTRL_ID_PREV_LEVEL2 2 +#define SCREEN_CTRL_ID_NEXT_LEVEL2 3 +#define SCREEN_CTRL_ID_PREV_SCORE 4 +#define SCREEN_CTRL_ID_NEXT_SCORE 5 +#define SCREEN_CTRL_ID_PLAY_TAPE 6 +#define SCREEN_CTRL_ID_FIRST_LEVEL 7 +#define SCREEN_CTRL_ID_LAST_LEVEL 8 +#define SCREEN_CTRL_ID_LEVEL_NUMBER 9 +#define SCREEN_CTRL_ID_PREV_PLAYER 10 +#define SCREEN_CTRL_ID_NEXT_PLAYER 11 +#define SCREEN_CTRL_ID_INSERT_SOLUTION 12 +#define SCREEN_CTRL_ID_PLAY_SOLUTION 13 +#define SCREEN_CTRL_ID_LEVELSET_INFO 14 +#define SCREEN_CTRL_ID_SWITCH_ECS_AGA 15 +#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE 16 +#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE 17 +#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2 18 +#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2 19 + +#define NUM_SCREEN_MENUBUTTONS 20 + +#define SCREEN_CTRL_ID_SCROLL_UP 20 +#define SCREEN_CTRL_ID_SCROLL_DOWN 21 +#define SCREEN_CTRL_ID_SCROLL_VERTICAL 22 +#define SCREEN_CTRL_ID_NETWORK_SERVER 23 + +#define NUM_SCREEN_GADGETS 24 #define NUM_SCREEN_SCROLLBUTTONS 2 #define NUM_SCREEN_SCROLLBARS 1 -#define NUM_SCREEN_GADGETS 3 +#define NUM_SCREEN_TEXTINPUT 1 + +#define SCREEN_MASK_MAIN (1 << 0) +#define SCREEN_MASK_MAIN_HAS_SOLUTION (1 << 1) +#define SCREEN_MASK_MAIN_HAS_SET_INFO (1 << 2) +#define SCREEN_MASK_INPUT (1 << 3) +#define SCREEN_MASK_TOUCH (1 << 4) +#define SCREEN_MASK_TOUCH2 (1 << 5) +#define SCREEN_MASK_SCORES (1 << 6) +#define SCREEN_MASK_SCORES_INFO (1 << 7) + +// graphic position and size values for buttons and scrollbars +#define SC_MENUBUTTON_XSIZE TILEX +#define SC_MENUBUTTON_YSIZE TILEY + +#define SC_SCROLLBUTTON_XSIZE TILEX +#define SC_SCROLLBUTTON_YSIZE TILEY + +#define SC_SCROLLBAR_XPOS (SXSIZE - SC_SCROLLBUTTON_XSIZE) + +#define SC_SCROLL_VERTICAL_XSIZE SC_SCROLLBUTTON_XSIZE +#define SC_SCROLL_VERTICAL_YSIZE ((MAX_MENU_ENTRIES_ON_SCREEN - 2) * \ + SC_SCROLLBUTTON_YSIZE) + +#define SC_SCROLL_UP_XPOS SC_SCROLLBAR_XPOS +#define SC_SCROLL_UP_YPOS (2 * SC_SCROLLBUTTON_YSIZE) + +#define SC_SCROLL_VERTICAL_XPOS SC_SCROLLBAR_XPOS +#define SC_SCROLL_VERTICAL_YPOS (SC_SCROLL_UP_YPOS + \ + SC_SCROLLBUTTON_YSIZE) -/* forward declarations of internal functions */ +#define SC_SCROLL_DOWN_XPOS SC_SCROLLBAR_XPOS +#define SC_SCROLL_DOWN_YPOS (SC_SCROLL_VERTICAL_YPOS + \ + SC_SCROLL_VERTICAL_YSIZE) + +#define SC_BORDER_SIZE 14 + + +// forward declarations of internal functions static void HandleScreenGadgets(struct GadgetInfo *); static void HandleSetupScreen_Generic(int, int, int, int, int); static void HandleSetupScreen_Input(int, int, int, int, int); static void CustomizeKeyboard(int); -static void CalibrateJoystick(int); +static void ConfigureJoystick(int); +static void ConfigureVirtualButtons(void); +static void execSetupGame(void); +static void execSetupEngines(void); +static void execSetupGraphics(void); +static void execSetupSound(void); +static void execSetupTouch(void); static void execSetupArtwork(void); static void HandleChooseTree(int, int, int, int, int, TreeInfo **); +static void DrawChoosePlayerName(void); +static void DrawChooseLevelSet(void); +static void DrawChooseLevelNr(void); +static void DrawScoreInfo(int); +static void DrawScoreInfo_Content(int); +static void DrawInfoScreen(void); +static void DrawSetupScreen(void); +static void DrawTypeName(void); + +static void DrawInfoScreen_NotAvailable(char *, char *); +static void DrawInfoScreen_HelpAnim(int, int, boolean); +static void DrawInfoScreen_HelpText(int, int, int, int); +static void HandleInfoScreen_Main(int, int, int, int, int); +static void HandleInfoScreen_TitleScreen(int, int, int); +static void HandleInfoScreen_Elements(int, int, int); +static void HandleInfoScreen_Music(int, int, int); +static void HandleInfoScreen_Version(int); +static void HandleInfoScreen_Generic(int, int, int); + +static void ModifyGameSpeedIfNeeded(void); +static void DisableVsyncIfNeeded(void); + +static void RedrawScreenMenuGadgets(int); +static void MapScreenMenuGadgets(int); +static void UnmapScreenMenuGadgets(int); +static void MapScreenGadgets(int); +static void UnmapScreenGadgets(void); +static void MapScreenTreeGadgets(TreeInfo *); +static void UnmapScreenTreeGadgets(void); + +static void UpdateScreenMenuGadgets(int, boolean); +static void AdjustScoreInfoButtons_SelectScore(int, int, int); +static void AdjustScoreInfoButtons_PlayTape(int, int, boolean); + +static boolean OfferUploadTapes(void); +static void execOfferUploadTapes(void); + +static void DrawHallOfFame_setScoreEntries(void); +static void HandleHallOfFame_SelectLevel(int, int); +static char *getHallOfFameRankText(int, int); +static char *getHallOfFameScoreText(int, int); +static char *getInfoScreenTitle_Generic(void); +static int getInfoScreenBackgroundImage_Generic(void); +static int getInfoScreenBackgroundSound_Generic(void); +static int getInfoScreenBackgroundMusic_Generic(void); + +static struct TokenInfo *getSetupInfoFinal(struct TokenInfo *); + static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS]; + +static int info_mode = INFO_MODE_MAIN; static int setup_mode = SETUP_MODE_MAIN; -static void drawCursorExt(int pos, int color, int graphic) -{ - static int cursor_array[SCR_FIELDY]; +static boolean info_screens_from_main = FALSE; - if (graphic) - cursor_array[pos] = graphic; +static TreeInfo *window_sizes = NULL; +static TreeInfo *window_size_current = NULL; - graphic = cursor_array[pos]; +static TreeInfo *scaling_types = NULL; +static TreeInfo *scaling_type_current = NULL; - if (color == FC_RED) - graphic = (graphic == IMG_ARROW_BLUE_LEFT ? IMG_ARROW_RED_LEFT : - graphic == IMG_ARROW_BLUE_RIGHT ? IMG_ARROW_RED_RIGHT : - IMG_BALL_RED); +static TreeInfo *rendering_modes = NULL; +static TreeInfo *rendering_mode_current = NULL; - DrawGraphic(0, MENU_SCREEN_START_YPOS + pos, graphic, 0); -} +static TreeInfo *vsync_modes = NULL; +static TreeInfo *vsync_mode_current = NULL; + +static TreeInfo *scroll_delays = NULL; +static TreeInfo *scroll_delay_current = NULL; + +static TreeInfo *snapshot_modes = NULL; +static TreeInfo *snapshot_mode_current = NULL; + +static TreeInfo *scores_types = NULL; +static TreeInfo *scores_type_current = NULL; + +static TreeInfo *game_speeds_normal = NULL; +static TreeInfo *game_speeds_extended = NULL; +static TreeInfo *game_speeds = NULL; +static TreeInfo *game_speed_current = NULL; + +static TreeInfo *volumes_simple = NULL; +static TreeInfo *volume_simple_current = NULL; + +static TreeInfo *volumes_loops = NULL; +static TreeInfo *volume_loops_current = NULL; + +static TreeInfo *volumes_music = NULL; +static TreeInfo *volume_music_current = NULL; + +static TreeInfo *touch_controls = NULL; +static TreeInfo *touch_control_current = NULL; -static void initCursor(int pos, int graphic) +static TreeInfo *move_distances = NULL; +static TreeInfo *move_distance_current = NULL; + +static TreeInfo *drop_distances = NULL; +static TreeInfo *drop_distance_current = NULL; + +static TreeInfo *transparencies = NULL; +static TreeInfo *transparency_current = NULL; + +static TreeInfo *grid_sizes[2][2] = { { NULL, NULL }, { NULL, NULL } }; +static TreeInfo *grid_size_current[2][2] = { { NULL, NULL }, { NULL, NULL } }; + +static TreeInfo *player_name = NULL; +static TreeInfo *player_name_current = NULL; + +static TreeInfo *level_number = NULL; +static TreeInfo *level_number_current = NULL; + +static TreeInfo *score_entries = NULL; +static TreeInfo *score_entry_current = NULL; + +static struct ValueTextInfo window_sizes_list[] = { - drawCursorExt(pos, FC_BLUE, graphic); -} + { 50, "50 %" }, + { 80, "80 %" }, + { 90, "90 %" }, + { 100, "100 % (Default)" }, + { 110, "110 %" }, + { 120, "120 %" }, + { 130, "130 %" }, + { 140, "140 %" }, + { 150, "150 %" }, + { 200, "200 %" }, + { 250, "250 %" }, + { 300, "300 %" }, + + { -1, NULL }, +}; -static void drawCursor(int pos, int color) +static struct StringValueTextInfo scaling_types_list[] = { - drawCursorExt(pos, color, 0); -} + { SCALING_QUALITY_NEAREST, "Off" }, + { SCALING_QUALITY_LINEAR, "Linear" }, + { SCALING_QUALITY_BEST, "Anisotropic" }, + + { NULL, NULL }, +}; -void DrawHeadline() +static struct StringValueTextInfo rendering_modes_list[] = { - int x = SX + (SXSIZE - strlen(PROGRAM_TITLE_STRING) * FONT1_XSIZE) / 2; + { STR_SPECIAL_RENDERING_OFF, "Off (May show artifacts, fast)" }, + { STR_SPECIAL_RENDERING_BITMAP, "Bitmap/Texture mode (slower)" }, +#if DEBUG + // this mode may work under certain conditions, but does not work on Windows + { STR_SPECIAL_RENDERING_TARGET, "Target Texture mode (slower)" }, +#endif + { STR_SPECIAL_RENDERING_DOUBLE, "Double Texture mode (slower)" }, - DrawText(x, SY + 8, PROGRAM_TITLE_STRING, FS_BIG, FC_YELLOW); - DrawTextFCentered(46, FC_RED, WINDOW_SUBTITLE_STRING); -} + { NULL, NULL }, +}; -static void ToggleFullscreenIfNeeded() +static struct StringValueTextInfo vsync_modes_list[] = { - if (setup.fullscreen != video.fullscreen_enabled) - { - /* save old door content */ - BlitBitmap(backbuffer, pix[PIX_DB_DOOR], - DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1); + { STR_VSYNC_MODE_OFF, "Off" }, + { STR_VSYNC_MODE_NORMAL, "Normal" }, + { STR_VSYNC_MODE_ADAPTIVE, "Adaptive" }, - /* toggle fullscreen */ - ChangeVideoModeIfNeeded(setup.fullscreen); - setup.fullscreen = video.fullscreen_enabled; + { NULL, NULL }, +}; - /* redraw background to newly created backbuffer */ - BlitBitmap(pix[PIX_BACK], backbuffer, 0,0, WIN_XSIZE,WIN_YSIZE, 0,0); +static struct StringValueTextInfo scores_types_list[] = +{ + { STR_SCORES_TYPE_LOCAL_ONLY, "Local scores only" }, + { STR_SCORES_TYPE_SERVER_ONLY, "Server scores only" }, + { STR_SCORES_TYPE_LOCAL_AND_SERVER, "Local and server scores" }, - /* restore old door content */ - BlitBitmap(pix[PIX_DB_DOOR], backbuffer, - DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY); + { NULL, NULL }, +}; - redraw_mask = REDRAW_ALL; - } -} +static struct ValueTextInfo game_speeds_list_normal[] = +{ + { 30, "Very Slow" }, + { 25, "Slow" }, + { 20, "Normal" }, + { 15, "Fast" }, + { 10, "Very Fast" }, + + { -1, NULL }, +}; -void DrawMainMenu() +static struct ValueTextInfo game_speeds_list_extended[] = { - static LevelDirTree *leveldir_last_valid = NULL; - int i; - char *name_text = (!options.network && setup.team_mode ? "Team:" : "Name:"); + { 1000, "1 fps (Extremely Slow)" }, + { 500, "2 fps" }, + { 200, "5 fps" }, + { 100, "10 fps" }, + { 50, "20 fps" }, + { 29, "35 fps (Original Supaplex)" }, + { 25, "40 fps" }, + { 20, "50 fps (=== Normal Speed ===)" }, + { 16, "60 fps (60 Hz VSync Speed)" }, + { 14, "70 fps (Maximum Supaplex)" }, + { 10, "100 fps" }, + { 5, "200 fps" }, + { 2, "500 fps" }, + { 1, "1000 fps (Extremely Fast)" }, + + { -1, NULL }, +}; - UnmapAllGadgets(); - FadeSounds(); - KeyboardAutoRepeatOn(); - ActivateJoystick(); - SetDrawDeactivationMask(REDRAW_NONE); - audio.sound_deactivated = FALSE; +static struct ValueTextInfo *game_speeds_list; - /* needed if last screen was the playing screen, invoked from level editor */ - if (level_editor_test_game) - { - game_status = LEVELED; - DrawLevelEd(); - return; - } +static struct ValueTextInfo scroll_delays_list[] = +{ + { 0, "0 Tiles (No Scroll Delay)" }, + { 1, "1 Tile" }, + { 2, "2 Tiles" }, + { 3, "3 Tiles (Default)" }, + { 4, "4 Tiles" }, + { 5, "5 Tiles" }, + { 6, "6 Tiles" }, + { 7, "7 Tiles" }, + { 8, "8 Tiles (Maximum Scroll Delay)"}, + + { -1, NULL }, +}; - /* needed if last screen was the editor screen */ - UndrawSpecialEditorDoor(); +static struct StringValueTextInfo snapshot_modes_list[] = +{ + { STR_SNAPSHOT_MODE_OFF, "Off" }, + { STR_SNAPSHOT_MODE_EVERY_STEP, "Every Step" }, + { STR_SNAPSHOT_MODE_EVERY_MOVE, "Every Move" }, + { STR_SNAPSHOT_MODE_EVERY_COLLECT, "Every Collect" }, - /* needed if last screen was the setup screen and fullscreen state changed */ - ToggleFullscreenIfNeeded(); + { NULL, NULL }, +}; - /* needed if last screen (level choice) changed graphics, sounds or music */ - ReloadCustomArtwork(); +static struct ValueTextInfo volumes_list[] = +{ + { 0, "0 %" }, + { 1, "1 %" }, + { 2, "2 %" }, + { 5, "5 %" }, + { 10, "10 %" }, + { 20, "20 %" }, + { 30, "30 %" }, + { 40, "40 %" }, + { 50, "50 %" }, + { 60, "60 %" }, + { 70, "70 %" }, + { 80, "80 %" }, + { 90, "90 %" }, + { 100, "100 %" }, + + { -1, NULL }, +}; -#ifdef TARGET_SDL - SetDrawtoField(DRAW_BACKBUFFER); -#endif +static struct StringValueTextInfo touch_controls_list[] = +{ + { TOUCH_CONTROL_OFF, "Off" }, + { TOUCH_CONTROL_VIRTUAL_BUTTONS, "Virtual Buttons" }, + { TOUCH_CONTROL_WIPE_GESTURES, "Wipe Gestures" }, + { TOUCH_CONTROL_FOLLOW_FINGER, "Follow Finger" }, - /* map gadgets for main menu screen */ - MapTapeButtons(); + { NULL, NULL }, +}; - /* leveldir_current may be invalid (level group, parent link) */ - if (!validLevelSeries(leveldir_current)) - leveldir_current = getFirstValidTreeInfoEntry(leveldir_last_valid); +static struct ValueTextInfo distances_list[] = +{ + { 1, "1 %" }, + { 2, "2 %" }, + { 3, "3 %" }, + { 4, "4 %" }, + { 5, "5 %" }, + { 10, "10 %" }, + { 15, "15 %" }, + { 20, "20 %" }, + { 25, "25 %" }, + + { -1, NULL }, +}; - /* store valid level series information */ - leveldir_last_valid = leveldir_current; +static struct ValueTextInfo transparencies_list[] = +{ + { 0, "0 % (Opaque)" }, + { 10, "10 %" }, + { 20, "20 %" }, + { 30, "30 %" }, + { 40, "40 %" }, + { 50, "50 %" }, + { 60, "60 %" }, + { 70, "70 %" }, + { 80, "80 %" }, + { 90, "90 %" }, + { 100, "100 % (Invisible)" }, + + { -1, NULL }, +}; - /* level_nr may have been set to value over handicap with level editor */ - if (setup.handicap && level_nr > leveldir_current->handicap_level) - level_nr = leveldir_current->handicap_level; +static struct ValueTextInfo grid_sizes_list[] = +{ + { 3, "3" }, + { 4, "4" }, + { 5, "5" }, + { 6, "6" }, + { 7, "7" }, + { 8, "8" }, + { 9, "9" }, + { 10, "10" }, + { 11, "11" }, + { 12, "12" }, + { 13, "13" }, + { 14, "14" }, + { 15, "15" }, + { 16, "16" }, + { 17, "17" }, + { 18, "18" }, + { 19, "19" }, + { 20, "20" }, + { 21, "21" }, + { 22, "22" }, + { 23, "23" }, + { 24, "24" }, + { 25, "25" }, + { 26, "26" }, + { 27, "27" }, + { 28, "28" }, + { 29, "29" }, + { 30, "30" }, + { 31, "31" }, + { 32, "32" }, + + { -1, NULL }, +}; - GetPlayerConfig(); - LoadLevel(level_nr); +static int align_xoffset = 0; +static int align_yoffset = 0; + +#define DRAW_MODE(s) ((s) >= GAME_MODE_MAIN && \ + (s) <= GAME_MODE_SETUP ? (s) : \ + (s) == GAME_MODE_PSEUDO_TYPENAME ? \ + GAME_MODE_MAIN : \ + (s) == GAME_MODE_PSEUDO_TYPENAMES ? \ + GAME_MODE_NAMES : GAME_MODE_DEFAULT) + +// (there are no draw offset definitions needed for INFO_MODE_TITLE) +#define DRAW_MODE_INFO(i) ((i) >= INFO_MODE_TITLE && \ + (i) <= INFO_MODE_LEVELSET ? (i) : \ + INFO_MODE_MAIN) + +#define DRAW_MODE_SETUP(i) ((i) >= SETUP_MODE_MAIN && \ + (i) <= SETUP_MODE_SHORTCUTS_5 ? (i) : \ + (i) >= SETUP_MODE_CHOOSE_GRAPHICS && \ + (i) <= SETUP_MODE_CHOOSE_MUSIC ? \ + SETUP_MODE_CHOOSE_ARTWORK : \ + SETUP_MODE_CHOOSE_OTHER) + +#define DRAW_XOFFSET_INFO(i) (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ? \ + menu.draw_xoffset[GAME_MODE_INFO] : \ + menu.draw_xoffset_info[DRAW_MODE_INFO(i)]) +#define DRAW_YOFFSET_INFO(i) (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ? \ + menu.draw_yoffset[GAME_MODE_INFO] : \ + menu.draw_yoffset_info[DRAW_MODE_INFO(i)]) +#define EXTRA_SPACING_INFO(i) (DRAW_MODE_INFO(i) == INFO_MODE_MAIN ? \ + menu.extra_spacing[GAME_MODE_INFO] : \ + menu.extra_spacing_info[DRAW_MODE_INFO(i)]) + +#define DRAW_XOFFSET_SETUP(i) (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \ + menu.draw_xoffset[GAME_MODE_SETUP] : \ + menu.draw_xoffset_setup[DRAW_MODE_SETUP(i)]) +#define DRAW_YOFFSET_SETUP(i) (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \ + menu.draw_yoffset[GAME_MODE_SETUP] : \ + menu.draw_yoffset_setup[DRAW_MODE_SETUP(i)]) +#define EXTRA_SPACING_SETUP(i) (DRAW_MODE_SETUP(i) == SETUP_MODE_MAIN ? \ + menu.extra_spacing[GAME_MODE_SETUP] : \ + menu.extra_spacing_setup[DRAW_MODE_SETUP(i)]) + +#define EXTRA_SPACING_SCORES(i) (EXTRA_SPACING_INFO(i)) + +#define EXTRA_SPACING_SCOREINFO(i) (menu.extra_spacing[GAME_MODE_SCOREINFO]) + +#define DRAW_XOFFSET(s) ((s) == GAME_MODE_INFO ? \ + DRAW_XOFFSET_INFO(info_mode) : \ + (s) == GAME_MODE_SETUP ? \ + DRAW_XOFFSET_SETUP(setup_mode) : \ + menu.draw_xoffset[DRAW_MODE(s)]) +#define DRAW_YOFFSET(s) ((s) == GAME_MODE_INFO ? \ + DRAW_YOFFSET_INFO(info_mode) : \ + (s) == GAME_MODE_SETUP ? \ + DRAW_YOFFSET_SETUP(setup_mode) : \ + menu.draw_yoffset[DRAW_MODE(s)]) +#define EXTRA_SPACING(s) ((s) == GAME_MODE_INFO ? \ + EXTRA_SPACING_INFO(info_mode) : \ + (s) == GAME_MODE_SETUP ? \ + EXTRA_SPACING_SETUP(setup_mode) : \ + (s) == GAME_MODE_SCORES ? \ + EXTRA_SPACING_SCORES(info_mode) : \ + menu.extra_spacing[DRAW_MODE(s)]) + +#define mSX (SX + DRAW_XOFFSET(game_status)) +#define mSY (SY + DRAW_YOFFSET(game_status)) + +#define amSX (mSX + align_xoffset) +#define amSY (mSY + align_yoffset) + +#define NUM_MENU_ENTRIES_ON_SCREEN (menu.list_size[game_status] > 2 ? \ + menu.list_size[game_status] : \ + MAX_MENU_ENTRIES_ON_SCREEN) + +#define IN_VIS_MENU(x, y) IN_FIELD(x, y, SCR_FIELDX, \ + NUM_MENU_ENTRIES_ON_SCREEN) + + +// title display and control definitions + +#define MAX_NUM_TITLE_SCREENS (2 * MAX_NUM_TITLE_IMAGES + \ + 2 * MAX_NUM_TITLE_MESSAGES) + +#define NO_DIRECT_LEVEL_SELECT (-1) + + +static int num_title_screens = 0; + +struct TitleControlInfo +{ + boolean is_image; + boolean initial; + boolean first; + int local_nr; + int sort_priority; +}; + +struct TitleControlInfo title_controls[MAX_NUM_TITLE_SCREENS]; + + +// main menu display and control definitions + +#define MAIN_CONTROL_NAME 0 +#define MAIN_CONTROL_LEVELS 1 +#define MAIN_CONTROL_SCORES 2 +#define MAIN_CONTROL_EDITOR 3 +#define MAIN_CONTROL_INFO 4 +#define MAIN_CONTROL_GAME 5 +#define MAIN_CONTROL_SETUP 6 +#define MAIN_CONTROL_QUIT 7 +#define MAIN_CONTROL_PREV_LEVEL 8 +#define MAIN_CONTROL_NEXT_LEVEL 9 +#define MAIN_CONTROL_FIRST_LEVEL 10 +#define MAIN_CONTROL_LAST_LEVEL 11 +#define MAIN_CONTROL_LEVEL_NUMBER 12 +#define MAIN_CONTROL_LEVEL_INFO_1 13 +#define MAIN_CONTROL_LEVEL_INFO_2 14 +#define MAIN_CONTROL_LEVEL_NAME 15 +#define MAIN_CONTROL_LEVEL_AUTHOR 16 +#define MAIN_CONTROL_LEVEL_YEAR 17 +#define MAIN_CONTROL_LEVEL_IMPORTED_FROM 18 +#define MAIN_CONTROL_LEVEL_IMPORTED_BY 19 +#define MAIN_CONTROL_LEVEL_TESTED_BY 20 +#define MAIN_CONTROL_TITLE_1 21 +#define MAIN_CONTROL_TITLE_2 22 +#define MAIN_CONTROL_TITLE_3 23 + +static char str_main_text_name[10]; +static char str_main_text_first_level[10]; +static char str_main_text_last_level[10]; +static char str_main_text_level_number[10]; + +static char network_server_hostname[MAX_SETUP_TEXT_INPUT_LEN + 1]; + +static char *main_text_name = str_main_text_name; +static char *main_text_first_level = str_main_text_first_level; +static char *main_text_last_level = str_main_text_last_level; +static char *main_text_level_number = str_main_text_level_number; +static char *main_text_levels = "Levelset"; +static char *main_text_scores = "Hall Of Fame"; +static char *main_text_editor = "Level Creator"; +static char *main_text_info = "Info Screen"; +static char *main_text_game = "Start Game"; +static char *main_text_setup = "Setup"; +static char *main_text_quit = "Quit"; +static char *main_text_level_name = level.name; +static char *main_text_level_author = level.author; +static char *main_text_level_year = NULL; +static char *main_text_level_imported_from = NULL; +static char *main_text_level_imported_by = NULL; +static char *main_text_level_tested_by = NULL; +static char *main_text_title_1 = NULL; +static char *main_text_title_2 = NULL; +static char *main_text_title_3 = NULL; + +extern char debug_xsn_mode[]; + +struct MainControlInfo +{ + int nr; + + struct MenuPosInfo *pos_button; + int button_graphic; - ClearWindow(); - DrawHeadline(); - DrawText(SX + 32, SY + 2*32, name_text, FS_BIG, FC_GREEN); - DrawText(SX + 6*32, SY + 2*32, setup.player_name, FS_BIG, FC_RED); - DrawText(SX + 32, SY + 3*32, "Level:", FS_BIG, FC_GREEN); - DrawText(SX + 11*32, SY + 3*32, int2str(level_nr,3), FS_BIG, - (leveldir_current->readonly ? FC_RED : FC_YELLOW)); - DrawText(SX + 32, SY + 4*32, "Hall Of Fame", FS_BIG, FC_GREEN); - DrawText(SX + 32, SY + 5*32, "Level Creator", FS_BIG, FC_GREEN); - DrawText(SY + 32, SY + 6*32, "Info Screen", FS_BIG, FC_GREEN); - DrawText(SX + 32, SY + 7*32, "Start Game", FS_BIG, FC_GREEN); - DrawText(SX + 32, SY + 8*32, "Setup", FS_BIG, FC_GREEN); - DrawText(SX + 32, SY + 9*32, "Quit", FS_BIG, FC_GREEN); + struct TextPosInfo *pos_text; + char **text; - DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, TRUE); + struct TextPosInfo *pos_input; + char **input; +}; - DrawTextF(7*32 + 6, 3*32 + 9, FC_RED, "%d-%d", - leveldir_current->first_level, - leveldir_current->last_level); +static struct MainControlInfo main_controls[] = +{ + { + MAIN_CONTROL_NAME, + &menu.main.button.name, IMG_MENU_BUTTON_NAME, + &menu.main.text.name, &main_text_name, + &menu.main.input.name, &setup.player_name, + }, + { + MAIN_CONTROL_LEVELS, + &menu.main.button.levels, IMG_MENU_BUTTON_LEVELS, + &menu.main.text.levels, &main_text_levels, + NULL, NULL, + }, + { + MAIN_CONTROL_SCORES, + &menu.main.button.scores, IMG_MENU_BUTTON_SCORES, + &menu.main.text.scores, &main_text_scores, + NULL, NULL, + }, + { + MAIN_CONTROL_EDITOR, + &menu.main.button.editor, IMG_MENU_BUTTON_EDITOR, + &menu.main.text.editor, &main_text_editor, + NULL, NULL, + }, + { + MAIN_CONTROL_INFO, + &menu.main.button.info, IMG_MENU_BUTTON_INFO, + &menu.main.text.info, &main_text_info, + NULL, NULL, + }, + { + MAIN_CONTROL_GAME, + &menu.main.button.game, IMG_MENU_BUTTON_GAME, + &menu.main.text.game, &main_text_game, + NULL, NULL, + }, + { + MAIN_CONTROL_SETUP, + &menu.main.button.setup, IMG_MENU_BUTTON_SETUP, + &menu.main.text.setup, &main_text_setup, + NULL, NULL, + }, + { + MAIN_CONTROL_QUIT, + &menu.main.button.quit, IMG_MENU_BUTTON_QUIT, + &menu.main.text.quit, &main_text_quit, + NULL, NULL, + }, + { + MAIN_CONTROL_PREV_LEVEL, + NULL, -1, + NULL, NULL, + NULL, NULL, + }, + { + MAIN_CONTROL_NEXT_LEVEL, + NULL, -1, + NULL, NULL, + NULL, NULL, + }, + { + MAIN_CONTROL_FIRST_LEVEL, + NULL, -1, + &menu.main.text.first_level, &main_text_first_level, + NULL, NULL, + }, + { + MAIN_CONTROL_LAST_LEVEL, + NULL, -1, + &menu.main.text.last_level, &main_text_last_level, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_NUMBER, + NULL, -1, + &menu.main.text.level_number, &main_text_level_number, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_INFO_1, + NULL, -1, + &menu.main.text.level_info_1, NULL, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_INFO_2, + NULL, -1, + &menu.main.text.level_info_2, NULL, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_NAME, + NULL, -1, + &menu.main.text.level_name, &main_text_level_name, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_AUTHOR, + NULL, -1, + &menu.main.text.level_author, &main_text_level_author, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_YEAR, + NULL, -1, + &menu.main.text.level_year, &main_text_level_year, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_IMPORTED_FROM, + NULL, -1, + &menu.main.text.level_imported_from, &main_text_level_imported_from, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_IMPORTED_BY, + NULL, -1, + &menu.main.text.level_imported_by, &main_text_level_imported_by, + NULL, NULL, + }, + { + MAIN_CONTROL_LEVEL_TESTED_BY, + NULL, -1, + &menu.main.text.level_tested_by, &main_text_level_tested_by, + NULL, NULL, + }, + { + MAIN_CONTROL_TITLE_1, + NULL, -1, + &menu.main.text.title_1, &main_text_title_1, + NULL, NULL, + }, + { + MAIN_CONTROL_TITLE_2, + NULL, -1, + &menu.main.text.title_2, &main_text_title_2, + NULL, NULL, + }, + { + MAIN_CONTROL_TITLE_3, + NULL, -1, + &menu.main.text.title_3, &main_text_title_3, + NULL, NULL, + }, - if (leveldir_current->readonly) { - DrawTextF(15*32 + 6, 3*32 + 9 - 7, FC_RED, "READ"); - DrawTextF(15*32 + 6, 3*32 + 9 + 7, FC_RED, "ONLY"); + -1, + NULL, -1, + NULL, NULL, + NULL, NULL, } +}; - for(i=0; i<8; i++) - initCursor(i, (i == 1 || i == 6 ? IMG_ARROW_BLUE_RIGHT : IMG_BALL_BLUE)); - DrawGraphic(10, 3, IMG_ARROW_BLUE_LEFT, 0); - DrawGraphic(14, 3, IMG_ARROW_BLUE_RIGHT, 0); +static boolean hasLevelSetInfo(void) +{ + return (getLevelSetInfoFilename(0) != NULL); +} - DrawText(SX + 56, SY + 326, "A Game by Artsoft Entertainment", - FS_SMALL, FC_RED); +static int getTitleScreenGraphic(int nr, boolean initial) +{ + return (initial ? IMG_TITLESCREEN_INITIAL_1 : IMG_TITLESCREEN_1) + nr; +} - if (leveldir_current->name) - { - int len = strlen(leveldir_current->name); - int lxpos = SX + (SXSIZE - len * FONT4_XSIZE) / 2; - int lypos = SY + 352; +static struct TitleMessageInfo *getTitleMessageInfo(int nr, boolean initial) +{ + return (initial ? &titlemessage_initial[nr] : &titlemessage[nr]); +} - DrawText(lxpos, lypos, leveldir_current->name, FS_SMALL, FC_SPECIAL2); - } +#if 0 +static int getTitleScreenGameMode(boolean initial) +{ + return (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE); +} +#endif + +static int getTitleMessageGameMode(boolean initial) +{ + return (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE); +} - FadeToFront(); - InitAnimation(); - HandleMainMenu(0,0, 0,0, MB_MENU_INITIALIZE); +static int getTitleAnimMode(struct TitleControlInfo *tci) +{ + int base = (tci->initial ? GAME_MODE_TITLE_INITIAL_1 : GAME_MODE_TITLE_1); - TapeStop(); - if (TAPE_IS_EMPTY(tape)) - LoadTape(level_nr); - DrawCompleteVideoDisplay(); + return base + tci->local_nr; +} - OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2); +#if 0 +static int getTitleScreenBackground(boolean initial) +{ + return (initial ? IMG_BACKGROUND_TITLE_INITIAL : IMG_BACKGROUND_TITLE); +} +#endif #if 0 - ClearEventQueue(); +static int getTitleMessageBackground(int nr, boolean initial) +{ + return (initial ? IMG_BACKGROUND_TITLE_INITIAL : IMG_BACKGROUND_TITLE); +} #endif + +static int getTitleBackground(int nr, boolean initial, boolean is_image) +{ + int base = (is_image ? + (initial ? IMG_BACKGROUND_TITLESCREEN_INITIAL_1 : + IMG_BACKGROUND_TITLESCREEN_1) : + (initial ? IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1 : + IMG_BACKGROUND_TITLEMESSAGE_1)); + int graphic_global = (initial ? IMG_BACKGROUND_TITLE_INITIAL : + IMG_BACKGROUND_TITLE); + int graphic_local = base + nr; + + if (graphic_info[graphic_local].bitmap != NULL) + return graphic_local; + + if (graphic_info[graphic_global].bitmap != NULL) + return graphic_global; + + return IMG_UNDEFINED; } -static void gotoTopLevelDir() +static int getTitleSound(struct TitleControlInfo *tci) { - /* move upwards to top level directory */ - while (leveldir_current->node_parent) - { - /* write a "path" into level tree for easy navigation to last level */ - if (leveldir_current->node_parent->node_group->cl_first == -1) - { - int num_leveldirs = numTreeInfoInGroup(leveldir_current); - int leveldir_pos = posTreeInfo(leveldir_current); - int num_page_entries; - int cl_first, cl_cursor; + boolean is_image = tci->is_image; + int initial = tci->initial; + int nr = tci->local_nr; + int mode = (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE); + int base = (is_image ? + (initial ? SND_BACKGROUND_TITLESCREEN_INITIAL_1 : + SND_BACKGROUND_TITLESCREEN_1) : + (initial ? SND_BACKGROUND_TITLEMESSAGE_INITIAL_1 : + SND_BACKGROUND_TITLEMESSAGE_1)); + int sound_global = menu.sound[mode]; + int sound_local = base + nr; - if (num_leveldirs <= MAX_MENU_ENTRIES_ON_SCREEN) - num_page_entries = num_leveldirs; - else - num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1; +#if 0 + Debug("screens:getTitleSound", "%d, %d, %d: %d ['%s'], %d ['%s']", + nr, initial, is_image, + sound_global, getSoundListEntry(sound_global)->filename, + sound_local, getSoundListEntry(sound_local)->filename); +#endif - cl_first = MAX(0, leveldir_pos - num_page_entries + 1); - cl_cursor = leveldir_pos - cl_first; + if (!strEqual(getSoundListEntry(sound_local)->filename, UNDEFINED_FILENAME)) + return sound_local; - leveldir_current->node_parent->node_group->cl_first = cl_first; - leveldir_current->node_parent->node_group->cl_cursor = cl_cursor; - } + if (!strEqual(getSoundListEntry(sound_global)->filename, UNDEFINED_FILENAME)) + return sound_global; - leveldir_current = leveldir_current->node_parent; - } + return SND_UNDEFINED; } -void HandleMainMenu(int mx, int my, int dx, int dy, int button) +static int getTitleMusic(struct TitleControlInfo *tci) { - static int choice = 0; - int x = 0; - int y = choice; + boolean is_image = tci->is_image; + int initial = tci->initial; + int nr = tci->local_nr; + int mode = (initial ? GAME_MODE_TITLE_INITIAL : GAME_MODE_TITLE); + int base = (is_image ? + (initial ? MUS_BACKGROUND_TITLESCREEN_INITIAL_1 : + MUS_BACKGROUND_TITLESCREEN_1) : + (initial ? MUS_BACKGROUND_TITLEMESSAGE_INITIAL_1 : + MUS_BACKGROUND_TITLEMESSAGE_1)); + int music_global = menu.music[mode]; + int music_local = base + nr; - if (button == MB_MENU_INITIALIZE) - { - drawCursor(choice, FC_RED); - return; - } +#if 0 + Debug("screens:getTitleMusic", "%d, %d, %d: %d ['%s'], %d ['%s']", + nr, initial, is_image, + music_global, getMusicListEntry(music_global)->filename, + music_local, getMusicListEntry(music_local)->filename); +#endif + + if (!strEqual(getMusicListEntry(music_local)->filename, UNDEFINED_FILENAME)) + return music_local; + + if (!strEqual(getMusicListEntry(music_global)->filename, UNDEFINED_FILENAME)) + return music_global; + + return MUS_UNDEFINED; +} + +static struct TitleFadingInfo getTitleFading(struct TitleControlInfo *tci) +{ + boolean is_image = tci->is_image; + boolean initial = tci->initial; + boolean first = tci->first; + int nr = tci->local_nr; + struct TitleMessageInfo tmi; + struct TitleFadingInfo ti; + + tmi = (is_image ? (initial ? (first ? + titlescreen_initial_first[nr] : + titlescreen_initial[nr]) + : (first ? + titlescreen_first[nr] : + titlescreen[nr])) + : (initial ? (first ? + titlemessage_initial_first[nr] : + titlemessage_initial[nr]) + : (first ? + titlemessage_first[nr] : + titlemessage[nr]))); + + ti.fade_mode = tmi.fade_mode; + ti.fade_delay = tmi.fade_delay; + ti.post_delay = tmi.post_delay; + ti.auto_delay = tmi.auto_delay; + ti.auto_delay_unit = tmi.auto_delay_unit; + + return ti; +} + +static int compareTitleControlInfo(const void *object1, const void *object2) +{ + const struct TitleControlInfo *tci1 = (struct TitleControlInfo *)object1; + const struct TitleControlInfo *tci2 = (struct TitleControlInfo *)object2; + int compare_result; + + if (tci1->initial != tci2->initial) + compare_result = (tci1->initial ? -1 : +1); + else if (tci1->sort_priority != tci2->sort_priority) + compare_result = tci1->sort_priority - tci2->sort_priority; + else if (tci1->is_image != tci2->is_image) + compare_result = (tci1->is_image ? -1 : +1); + else + compare_result = tci1->local_nr - tci2->local_nr; + + return compare_result; +} + +static void InitializeTitleControlsExt_AddTitleInfo(boolean is_image, + boolean initial, + int nr, int sort_priority) +{ + title_controls[num_title_screens].is_image = is_image; + title_controls[num_title_screens].initial = initial; + title_controls[num_title_screens].local_nr = nr; + title_controls[num_title_screens].sort_priority = sort_priority; - if (mx || my) /* mouse input */ + title_controls[num_title_screens].first = FALSE; // will be set later + + num_title_screens++; +} + +static void InitializeTitleControls_CheckTitleInfo(boolean initial) +{ + int i; + + for (i = 0; i < MAX_NUM_TITLE_IMAGES; i++) { - x = (mx - SX) / 32; - y = (my - SY) / 32 - MENU_SCREEN_START_YPOS; + int graphic = getTitleScreenGraphic(i, initial); + Bitmap *bitmap = graphic_info[graphic].bitmap; + int sort_priority = graphic_info[graphic].sort_priority; + + if (bitmap != NULL) + InitializeTitleControlsExt_AddTitleInfo(TRUE, initial, i, sort_priority); } - else if (dx || dy) /* keyboard input */ + + for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++) { - if (dx && choice == 1) - x = (dx < 0 ? 10 : 14); - else if (dy) - y = choice + dy; + struct TitleMessageInfo *tmi = getTitleMessageInfo(i, initial); + char *filename = getLevelSetTitleMessageFilename(i, initial); + int sort_priority = tmi->sort_priority; + + if (filename != NULL) + InitializeTitleControlsExt_AddTitleInfo(FALSE, initial, i, sort_priority); } +} - if (y == 1 && ((x == 10 && level_nr > leveldir_current->first_level) || - (x == 14 && level_nr < leveldir_current->last_level)) && - button) - { - static unsigned long level_delay = 0; - int step = (button == 1 ? 1 : button == 2 ? 5 : 10); - int new_level_nr, old_level_nr = level_nr; - int font_color = (leveldir_current->readonly ? FC_RED : FC_YELLOW); +static void InitializeTitleControls(boolean show_title_initial) +{ + num_title_screens = 0; - new_level_nr = level_nr + (x == 10 ? -step : +step); - if (new_level_nr < leveldir_current->first_level) - new_level_nr = leveldir_current->first_level; - if (new_level_nr > leveldir_current->last_level) - new_level_nr = leveldir_current->last_level; + // 1st step: initialize title screens for game start (only when starting) + if (show_title_initial) + InitializeTitleControls_CheckTitleInfo(TRUE); - if (setup.handicap && new_level_nr > leveldir_current->handicap_level) - new_level_nr = leveldir_current->handicap_level; + // 2nd step: initialize title screens for current level set + InitializeTitleControls_CheckTitleInfo(FALSE); - if (old_level_nr == new_level_nr || - !DelayReached(&level_delay, GADGET_FRAME_DELAY)) - goto out; + // sort title screens according to sort_priority and title number + qsort(title_controls, num_title_screens, sizeof(struct TitleControlInfo), + compareTitleControlInfo); - level_nr = new_level_nr; + // mark first title screen + title_controls[0].first = TRUE; +} - DrawTextExt(drawto, SX + 11 * 32, SY + 3 * 32, - int2str(level_nr, 3), FS_BIG, font_color); - DrawTextExt(window, SX + 11 * 32, SY + 3 * 32, - int2str(level_nr, 3), FS_BIG, font_color); +static boolean visibleMenuPos(struct MenuPosInfo *pos) +{ + return (pos != NULL && pos->x != -1 && pos->y != -1); +} - LoadLevel(level_nr); - DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, TRUE); +static boolean visibleTextPos(struct TextPosInfo *pos) +{ + return (pos != NULL && pos->x != -1 && pos->y != -1); +} - TapeErase(); - LoadTape(level_nr); - DrawCompleteVideoDisplay(); +static void InitializeMainControls(void) +{ + boolean local_team_mode = (!network.enabled && setup.team_mode); + int i; - /* needed because DrawMicroLevel() takes some time */ - BackToFront(); - SyncDisplay(); - DelayReached(&level_delay, 0); /* reset delay counter */ - } - else if (x == 0 && y >= 0 && y <= 7) + // set main control text values to dynamically determined values + sprintf(main_text_name, "%s", local_team_mode ? "Team:" : "Name:"); + + strcpy(main_text_first_level, int2str(leveldir_current->first_level, + menu.main.text.first_level.size)); + strcpy(main_text_last_level, int2str(leveldir_current->last_level, + menu.main.text.last_level.size)); + strcpy(main_text_level_number, int2str(level_nr, + menu.main.text.level_number.size)); + + main_text_level_year = leveldir_current->year; + main_text_level_imported_from = leveldir_current->imported_from; + main_text_level_imported_by = leveldir_current->imported_by; + main_text_level_tested_by = leveldir_current->tested_by; + + main_text_title_1 = getConfigProgramTitleString(); + main_text_title_2 = getConfigProgramCopyrightString(); + main_text_title_3 = getConfigProgramCompanyString(); + + // set main control screen positions to dynamically determined values + for (i = 0; main_controls[i].nr != -1; i++) { - if (button) + struct MainControlInfo *mci = &main_controls[i]; + int nr = mci->nr; + struct MenuPosInfo *pos_button = mci->pos_button; + struct TextPosInfo *pos_text = mci->pos_text; + struct TextPosInfo *pos_input = mci->pos_input; + char *text = (mci->text ? *mci->text : NULL); + char *input = (mci->input ? *mci->input : NULL); + int button_graphic = mci->button_graphic; + int font_text = (pos_text ? pos_text->font : -1); + int font_input = (pos_input ? pos_input->font : -1); + + int font_text_width = (font_text != -1 ? getFontWidth(font_text) : 0); + int font_text_height = (font_text != -1 ? getFontHeight(font_text) : 0); + int font_input_width = (font_input != -1 ? getFontWidth(font_input) : 0); + int font_input_height = (font_input != -1 ? getFontHeight(font_input) : 0); + int text_chars = (text != NULL ? strlen(text) : 0); + int input_chars = (input != NULL ? strlen(input) : 0); + + int button_width = + (button_graphic != -1 ? graphic_info[button_graphic].width : 0); + int button_height = + (button_graphic != -1 ? graphic_info[button_graphic].height : 0); + int text_width = font_text_width * text_chars; + int text_height = font_text_height; + int input_width = font_input_width * input_chars; + int input_height = font_input_height; + + if (nr == MAIN_CONTROL_NAME) { - if (y != choice) - { - drawCursor(y, FC_RED); - drawCursor(choice, FC_BLUE); - choice = y; - } + menu.main.input.name.width = input_width; + menu.main.input.name.height = input_height; } - else + + if (pos_button != NULL) // (x/y may be -1/-1 here) { - if (y == 0) - { - game_status = TYPENAME; - HandleTypeName(strlen(setup.player_name), 0); - } - else if (y == 1) - { - if (leveldir_first) - { - game_status = CHOOSELEVEL; - SaveLevelSetup_LastSeries(); - SaveLevelSetup_SeriesInfo(); + pos_button->width = button_width; + pos_button->height = button_height; + } + + if (pos_text != NULL) // (x/y may be -1/-1 here) + { + // calculate text size -- needed for text alignment + boolean calculate_text_size = (text != NULL); - gotoTopLevelDir(); + if (pos_text->width == -1 || calculate_text_size) + pos_text->width = text_width; + if (pos_text->height == -1 || calculate_text_size) + pos_text->height = text_height; - DrawChooseLevel(); - } + if (visibleMenuPos(pos_button)) + { + if (pos_text->x == -1) + pos_text->x = pos_button->x + pos_button->width; + if (pos_text->y == -1) + pos_text->y = + pos_button->y + (pos_button->height - pos_text->height) / 2; } - else if (y == 2) + } + + if (pos_input != NULL) // (x/y may be -1/-1 here) + { + if (visibleTextPos(pos_text)) { - game_status = HALLOFFAME; - DrawHallOfFame(-1); + if (pos_input->x == -1) + pos_input->x = pos_text->x + pos_text->width; + if (pos_input->y == -1) + pos_input->y = pos_text->y; } - else if (y == 3) + + if (pos_input->width == -1) + pos_input->width = input_width; + if (pos_input->height == -1) + pos_input->height = input_height; + } + } +} + +static void DrawPressedGraphicThruMask(int dst_x, int dst_y, + int graphic, boolean pressed) +{ + struct GraphicInfo *g = &graphic_info[graphic]; + Bitmap *src_bitmap; + int src_x, src_y; + int xoffset = (pressed ? g->pressed_xoffset : 0); + int yoffset = (pressed ? g->pressed_yoffset : 0); + + getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y); + + BlitBitmapMasked(src_bitmap, drawto, src_x + xoffset, src_y + yoffset, + g->width, g->height, dst_x, dst_y); +} + +static void DrawCursorAndText_Main_Ext(int nr, boolean active_text, + boolean active_input, + boolean pressed_button) +{ + int i; + + for (i = 0; main_controls[i].nr != -1; i++) + { + struct MainControlInfo *mci = &main_controls[i]; + + if (mci->nr == nr || nr == -1) + { + struct MenuPosInfo *pos_button = mci->pos_button; + struct TextPosInfo *pos_text = mci->pos_text; + struct TextPosInfo *pos_input = mci->pos_input; + char *text = (mci->text ? *mci->text : NULL); + char *input = (mci->input ? *mci->input : NULL); + int button_graphic = mci->button_graphic; + int font_text = (pos_text ? pos_text->font : -1); + int font_input = (pos_input ? pos_input->font : -1); + + if (active_text) { - if (leveldir_current->readonly && - strcmp(setup.player_name, "Artsoft") != 0) - Request("This level is read only !", REQ_CONFIRM); - game_status = LEVELED; - DrawLevelEd(); + button_graphic = BUTTON_ACTIVE(button_graphic); + font_text = FONT_ACTIVE(font_text); } - else if (y == 4) + + if (active_input) { - game_status = HELPSCREEN; - DrawHelpScreen(); + font_input = FONT_ACTIVE(font_input); } - else if (y == 5) + + if (visibleMenuPos(pos_button)) { - if (setup.autorecord) - TapeStartRecording(); + struct MenuPosInfo *pos = pos_button; + int x = mSX + pos->x; + int y = mSY + pos->y; -#if defined(PLATFORM_UNIX) - if (options.network) - SendToServer_StartPlaying(); - else -#endif - { - game_status = PLAYING; - StopAnimation(); - InitGame(); - } + DrawBackgroundForGraphic(x, y, pos->width, pos->height, button_graphic); + DrawPressedGraphicThruMask(x, y, button_graphic, pressed_button); } - else if (y == 6) + + if (visibleTextPos(pos_text) && text != NULL) { - game_status = SETUP; - setup_mode = SETUP_MODE_MAIN; - DrawSetupScreen(); + struct TextPosInfo *pos = pos_text; + int x = mSX + ALIGNED_TEXT_XPOS(pos); + int y = mSY + ALIGNED_TEXT_YPOS(pos); + +#if 1 + // (check why/if this is needed) + DrawBackgroundForFont(x, y, pos->width, pos->height, font_text); +#endif + DrawText(x, y, text, font_text); } - else if (y == 7) + + if (visibleTextPos(pos_input) && input != NULL) { - SaveLevelSetup_LastSeries(); - SaveLevelSetup_SeriesInfo(); - if (Request("Do you really want to quit ?", REQ_ASK | REQ_STAY_CLOSED)) - game_status = EXITGAME; + struct TextPosInfo *pos = pos_input; + int x = mSX + ALIGNED_TEXT_XPOS(pos); + int y = mSY + ALIGNED_TEXT_YPOS(pos); + +#if 1 + // (check why/if this is needed) + DrawBackgroundForFont(x, y, pos->width, pos->height, font_input); +#endif + DrawText(x, y, input, font_input); } } } +} - BackToFront(); +static void DrawCursorAndText_Main(int nr, boolean active_text, + boolean pressed_button) +{ + DrawCursorAndText_Main_Ext(nr, active_text, FALSE, pressed_button); +} - out: - - if (game_status == MAINMENU) - { - DrawMicroLevel(MICROLEV_XPOS, MICROLEV_YPOS, FALSE); - DoAnimation(); - } -} - - -#define MAX_HELPSCREEN_ELS 10 -#define HA_NEXT -999 -#define HA_END -1000 - -static long helpscreen_state; -static int helpscreen_step[MAX_HELPSCREEN_ELS]; -static int helpscreen_frame[MAX_HELPSCREEN_ELS]; -static int helpscreen_delay[MAX_HELPSCREEN_ELS]; -static int helpscreen_action[] = -{ - GFX_SPIELER1_DOWN,4,2, - GFX_SPIELER1_UP,4,2, - GFX_SPIELER1_LEFT,4,2, - GFX_SPIELER1_RIGHT,4,2, - GFX_SPIELER1_PUSH_LEFT,4,2, - GFX_SPIELER1_PUSH_RIGHT,4,2, HA_NEXT, - GFX_ERDREICH,1,100, HA_NEXT, - GFX_LEERRAUM,1,100, HA_NEXT, - GFX_MORAST_LEER,1,100, HA_NEXT, - GFX_BETON,1,100, HA_NEXT, - GFX_MAUERWERK,1,100, HA_NEXT, - GFX_MAUER_L1, 3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10, - GFX_MAUER_R1, 3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10, - GFX_MAUER_UP, 3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10, - GFX_MAUER_DOWN,3,4, GFX_MAUERWERK,1,20, GFX_LEERRAUM,1,10, HA_NEXT, - GFX_UNSICHTBAR,1,100, HA_NEXT, - GFX_FELSBODEN,1,100, HA_NEXT, - GFX_CHAR_A,30,4, GFX_CHAR_AUSRUF,32,4, HA_NEXT, - GFX_EDELSTEIN,2,5, HA_NEXT, - GFX_DIAMANT,2,5, HA_NEXT, - GFX_EDELSTEIN_BD,2,5, HA_NEXT, - GFX_EDELSTEIN_GELB,2,5, GFX_EDELSTEIN_ROT,2,5, - GFX_EDELSTEIN_LILA,2,5, HA_NEXT, - GFX_FELSBROCKEN,4,5, HA_NEXT, - GFX_BOMBE,1,50, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10, HA_NEXT, - GFX_KOKOSNUSS,1,50, GFX_CRACKINGNUT,3,1, GFX_EDELSTEIN,1,10, HA_NEXT, - GFX_ERZ_EDEL,1,50, GFX_EXPLOSION,8,1, GFX_EDELSTEIN,1,10, HA_NEXT, - GFX_ERZ_DIAM,1,50, GFX_EXPLOSION,8,1, GFX_DIAMANT,1,10, HA_NEXT, - GFX_ERZ_EDEL_BD,1,50, GFX_EXPLOSION,8,1,GFX_EDELSTEIN_BD,1,10,HA_NEXT, - GFX_ERZ_EDEL_GELB,1,50, GFX_EXPLOSION,8,1, - GFX_EDELSTEIN_GELB,1,10, GFX_ERZ_EDEL_ROT,1,50, - GFX_EXPLOSION,8,1, GFX_EDELSTEIN_ROT,1,10, - GFX_ERZ_EDEL_LILA,1,50, GFX_EXPLOSION,8,1, - GFX_EDELSTEIN_LILA,1,10, HA_NEXT, - GFX_GEBLUBBER,4,4, HA_NEXT, - GFX_SCHLUESSEL1,4,25, HA_NEXT, - GFX_PFORTE1,4,25, HA_NEXT, - GFX_PFORTE1X,4,25, HA_NEXT, - GFX_DYNAMIT_AUS,1,100, HA_NEXT, - GFX_DYNAMIT,7,6, GFX_EXPLOSION,8,1, GFX_LEERRAUM,1,10, HA_NEXT, - GFX_DYNABOMB+0,4,3, GFX_DYNABOMB+3,1,3, GFX_DYNABOMB+2,1,3, - GFX_DYNABOMB+1,1,3, GFX_DYNABOMB+0,1,3, GFX_EXPLOSION,8,1, - GFX_LEERRAUM,1,10, HA_NEXT, - GFX_DYNABOMB_NR,1,100, HA_NEXT, - GFX_DYNABOMB_SZ,1,100, HA_NEXT, - GFX_FLIEGER+4,1,3, GFX_FLIEGER+0,1,3, GFX_FLIEGER+4,1,3, - GFX_FLIEGER+5,1,3, GFX_FLIEGER+1,1,3, GFX_FLIEGER+5,1,3, - GFX_FLIEGER+6,1,3, GFX_FLIEGER+2,1,3, GFX_FLIEGER+6,1,3, - GFX_FLIEGER+7,1,3, GFX_FLIEGER+3,1,3, GFX_FLIEGER+7,1,3, HA_NEXT, - GFX_KAEFER+4,1,1, GFX_KAEFER+0,1,1, GFX_KAEFER+4,1,1, - GFX_KAEFER+5,1,1, GFX_KAEFER+1,1,1, GFX_KAEFER+5,1,1, - GFX_KAEFER+6,1,1, GFX_KAEFER+2,1,1, GFX_KAEFER+6,1,1, - GFX_KAEFER+7,1,1, GFX_KAEFER+3,1,1, GFX_KAEFER+7,1,1, HA_NEXT, - GFX_BUTTERFLY,2,2, HA_NEXT, - GFX_FIREFLY,2,2, HA_NEXT, - GFX_PACMAN+0,1,3, GFX_PACMAN+4,1,2, GFX_PACMAN+0,1,3, - GFX_PACMAN+1,1,3, GFX_PACMAN+5,1,2, GFX_PACMAN+1,1,3, - GFX_PACMAN+2,1,3, GFX_PACMAN+6,1,2, GFX_PACMAN+2,1,3, - GFX_PACMAN+3,1,3, GFX_PACMAN+7,1,2, GFX_PACMAN+3,1,3, HA_NEXT, - GFX_MAMPFER+0,4,1, GFX_MAMPFER+3,1,1, GFX_MAMPFER+2,1,1, - GFX_MAMPFER+1,1,1, GFX_MAMPFER+0,1,1, HA_NEXT, - GFX_MAMPFER2+0,4,1, GFX_MAMPFER2+3,1,1, GFX_MAMPFER2+2,1,1, - GFX_MAMPFER2+1,1,1, GFX_MAMPFER2+0,1,1, HA_NEXT, - GFX_ROBOT+0,4,1, GFX_ROBOT+3,1,1, GFX_ROBOT+2,1,1, - GFX_ROBOT+1,1,1, GFX_ROBOT+0,1,1, HA_NEXT, - GFX_MOLE_DOWN,4,2, - GFX_MOLE_UP,4,2, - GFX_MOLE_LEFT,4,2, - GFX_MOLE_RIGHT,4,2, HA_NEXT, - GFX_PINGUIN_DOWN,4,2, - GFX_PINGUIN_UP,4,2, - GFX_PINGUIN_LEFT,4,2, - GFX_PINGUIN_RIGHT,4,2, HA_NEXT, - GFX_SCHWEIN_DOWN,4,2, - GFX_SCHWEIN_UP,4,2, - GFX_SCHWEIN_LEFT,4,2, - GFX_SCHWEIN_RIGHT,4,2, HA_NEXT, - GFX_DRACHE_DOWN,4,2, - GFX_DRACHE_UP,4,2, - GFX_DRACHE_LEFT,4,2, - GFX_DRACHE_RIGHT,4,2, HA_NEXT, - GFX_SONDE_START,8,1, HA_NEXT, - GFX_ABLENK,4,1, HA_NEXT, - GFX_BIRNE_AUS,1,25, GFX_BIRNE_EIN,1,25, HA_NEXT, - GFX_ZEIT_VOLL,1,25, GFX_ZEIT_LEER,1,25, HA_NEXT, - GFX_TROPFEN,1,25, GFX_AMOEBING,4,1, GFX_AMOEBE_LEBT,1,10, HA_NEXT, - GFX_AMOEBE_TOT+2,2,50, GFX_AMOEBE_TOT,2,50, HA_NEXT, - GFX_AMOEBE_LEBT,4,40, HA_NEXT, - GFX_AMOEBE_LEBT,1,10, GFX_AMOEBING,4,2, HA_NEXT, - GFX_AMOEBE_LEBT,1,25, GFX_AMOEBE_TOT,1,25, GFX_EXPLOSION,8,1, - GFX_DIAMANT,1,10, HA_NEXT, - GFX_LIFE,1,100, HA_NEXT, - GFX_LIFE_ASYNC,1,100, HA_NEXT, - GFX_MAGIC_WALL_OFF,4,2, HA_NEXT, - GFX_MAGIC_WALL_BD_OFF,4,2, HA_NEXT, - GFX_AUSGANG_ZU,1,100, GFX_AUSGANG_ACT,4,2, - GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2, - GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2, HA_NEXT, - GFX_AUSGANG_AUF+0,4,2, GFX_AUSGANG_AUF+3,1,2, - GFX_AUSGANG_AUF+2,1,2, GFX_AUSGANG_AUF+1,1,2, HA_NEXT, - GFX_SOKOBAN_OBJEKT,1,100, HA_NEXT, - GFX_SOKOBAN_FELD_LEER,1,100, HA_NEXT, - GFX_SOKOBAN_FELD_VOLL,1,100, HA_NEXT, - GFX_SPEED_PILL,1,100, HA_NEXT, - HA_END -}; -static char *helpscreen_eltext[][2] = -{ - {"THE HERO:", "(Is _this_ guy good old Rockford?)"}, - {"Normal sand:", "You can dig through it"}, - {"Empty field:", "You can walk through it"}, - {"Quicksand: You cannot pass it,", "but rocks can fall though it"}, - {"Massive Wall:", "Nothing can go through it"}, - {"Normal Wall: You can't go through", "it, but you can bomb it away"}, - {"Growing Wall: Grows in several di-", "rections if there is an empty field"}, - {"Invisible Wall: Behaves like normal","wall, but is invisible"}, - {"Old Wall: Like normal wall, but", "some things can fall down from it"}, - {"Letter Wall: Looks like a letter,", "behaves like a normal wall"}, - {"Emerald: You must collect enough of","them to finish a level"}, - {"Diamond: Counts as 3 emeralds, but", "can be destroyed by rocks"}, - {"Diamond (BD style): Counts like one","emerald and behaves a bit different"}, - {"Colorful Gems:", "Seem to behave like Emeralds"}, - {"Rock: Smashes several things;", "Can be moved by the player"}, - {"Bomb: You can move it, but be", "careful when dropping it"}, - {"Nut: Throw a rock on it to open it;","Each nut contains an emerald"}, - {"Wall with an emerald inside:", "Bomb the wall away to get it"}, - {"Wall with a diamond inside:", "Bomb the wall away to get it"}, - {"Wall with BD style diamond inside:", "Bomb the wall away to get it"}, - {"Wall with colorful gem inside:", "Bomb the wall away to get it"}, - {"Acid: Things that fall in are gone", "forever (including our hero)"}, - {"Key: Opens the door that has the", "same color (red/yellow/green/blue)"}, - {"Door: Can be opened by the key", "with the same color"}, - {"Door: You have to find out the", "right color of the key for it"}, - {"Dynamite: Collect it and use it to", "destroy walls or kill enemies"}, - {"Dynamite: This one explodes after", "a few seconds"}, - {"Dyna Bomb: Explodes in 4 directions","with variable explosion size"}, - {"Dyna Bomb: Increases the number of", "dyna bombs available at a time"}, - {"Dyna Bomb: Increases the size of", "explosion of dyna bombs"}, - {"Spaceship: Moves at the left side", "of walls; don't touch it!"}, - {"Bug: Moves at the right side", "of walls; don't touch it!"}, - {"Butterfly: Moves at the right side", "of walls; don't touch it!"}, - {"Firefly: Moves at the left side", "of walls; don't touch it!"}, - {"Pacman: Eats the amoeba and you,", "if you're not careful"}, - {"Cruncher: Eats diamonds and you,", "if you're not careful"}, - {"Cruncher (BD style):", "Eats almost everything"}, - {"Robot: Tries to kill the player", ""}, - {"The mole: Eats the amoeba and turns","empty space into normal sand"}, - {"The penguin: Guide him to the exit,","but keep him away from monsters!"}, - {"The Pig: Harmless, but eats all", "gems it can get"}, - {"The Dragon: Breathes fire,", "especially to some monsters"}, - {"Sonde: Follows you everywhere;", "harmless, but may block your way"}, - {"Magic Wheel: Touch it to get rid of","the robots for some seconds"}, - {"Light Bulb: All of them must be", "switched on to finish a level"}, - {"Extra Time Orb: Adds some seconds", "to the time available for the level"}, - {"Amoeba Drop: Grows to an amoeba on", "the ground - don't touch it"}, - {"Dead Amoeba: Does not grow, but", "can still kill bugs and spaceships"}, - {"Normal Amoeba: Grows through empty", "fields, sand and quicksand"}, - {"Dropping Amoeba: This one makes", "drops that grow to a new amoeba"}, - {"Living Amoeba (BD style): Contains", "other element, when surrounded"}, - {"Game Of Life: Behaves like the well","known 'Game Of Life' (2333 style)"}, - {"Biomaze: A bit like the 'Game Of", "Life', but builds crazy mazes"}, - {"Magic Wall: Changes rocks, emeralds","and diamonds when they pass it"}, - {"Magic Wall (BD style):", "Changes rocks and BD style diamonds"}, - {"Exit door: Opens if you have enough","emeralds to finish the level"}, - {"Open exit door: Enter here to leave","the level and exit the actual game"}, - {"Sokoban element: Object which must", "be pushed to an empty field"}, - {"Sokoban element: Empty field where", "a Sokoban object can be placed on"}, - {"Sokoban element: Field with object", "which can be pushed away"}, - {"Speed pill: Lets the player run", "twice as fast as normally"}, -}; -static int num_helpscreen_els = sizeof(helpscreen_eltext)/(2*sizeof(char *)); +#if 0 +static void DrawCursorAndText_Main_Input(int nr, boolean active_text, + boolean pressed_button) +{ + DrawCursorAndText_Main_Ext(nr, active_text, TRUE, pressed_button); +} +#endif -static char *helpscreen_music[][3] = +static struct MainControlInfo *getMainControlInfo(int nr) { - { "Alchemy", "Ian Boddy", "Drive" }, - { "The Chase", "Propaganda", "A Secret Wish" }, - { "Network 23", "Tangerine Dream", "Exit" }, - { "Czardasz", "Robert Pieculewicz", "Czardasz" }, - { "21st Century Common Man", "Tangerine Dream", "Tyger" }, - { "Voyager", "The Alan Parsons Project","Pyramid" }, - { "Twilight Painter", "Tangerine Dream", "Heartbreakers" } -}; -static int num_helpscreen_music = 7; -static int helpscreen_musicpos; + int i; + + for (i = 0; main_controls[i].nr != -1; i++) + if (main_controls[i].nr == nr) + return &main_controls[i]; + + return NULL; +} -void DrawHelpScreenElAction(int start) +static boolean insideMenuPosRect(struct MenuPosInfo *rect, int x, int y) { - int i = 0, j = 0; - int frame, graphic; - int xstart = SX+16, ystart = SY+64+2*32, ystep = TILEY+4; + if (rect == NULL) + return FALSE; - while(helpscreen_action[j] != HA_END) - { - if (i>=start+MAX_HELPSCREEN_ELS || i>=num_helpscreen_els) - break; - else if (i=start && helpscreen_delay[i-start]) - helpscreen_delay[i-start]--; + int rect_x = ALIGNED_TEXT_XPOS(rect); + int rect_y = ALIGNED_TEXT_YPOS(rect); - while(helpscreen_action[j] != HA_NEXT) - j++; - j++; - i++; - continue; - } + return (x >= rect_x && x < rect_x + rect->width && + y >= rect_y && y < rect_y + rect->height); +} - j += 3*helpscreen_step[i-start]; - graphic = helpscreen_action[j++]; +static boolean insideTextPosRect(struct TextPosInfo *rect, int x, int y) +{ + if (rect == NULL) + return FALSE; - if (helpscreen_frame[i-start]) - { - frame = helpscreen_action[j++] - helpscreen_frame[i-start]; - helpscreen_frame[i-start]--; - } - else - { - frame = 0; - helpscreen_frame[i-start] = helpscreen_action[j++]-1; - } + int rect_x = ALIGNED_TEXT_XPOS(rect); + int rect_y = ALIGNED_TEXT_YPOS(rect); - helpscreen_delay[i-start] = helpscreen_action[j++] - 1; +#if 0 + Debug("screens:insideTextPosRect", + "(%d, %d), (%d, %d) [%d, %d] (%d, %d) => %d", + x, y, rect_x, rect_y, rect->x, rect->y, rect->width, rect->height, + (x >= rect_x && x < rect_x + rect->width && + y >= rect_y && y < rect_y + rect->height)); +#endif - if (helpscreen_action[j] == HA_NEXT) - { - if (!helpscreen_frame[i-start]) - helpscreen_step[i-start] = 0; - } - else - { - if (!helpscreen_frame[i-start]) - helpscreen_step[i-start]++; - while(helpscreen_action[j] != HA_NEXT) - j++; - } - j++; + return (x >= rect_x && x < rect_x + rect->width && + y >= rect_y && y < rect_y + rect->height); +} - DrawOldGraphicExt(drawto, xstart, ystart+(i-start)*ystep, graphic+frame); - i++; - } +static boolean insidePreviewRect(struct PreviewInfo *preview, int x, int y) +{ + int rect_width = preview->xsize * preview->tile_size; + int rect_height = preview->ysize * preview->tile_size; + int rect_x = ALIGNED_XPOS(preview->x, rect_width, preview->align); + int rect_y = ALIGNED_YPOS(preview->y, rect_height, preview->valign); - for(i=2;i<16;i++) - { - MarkTileDirty(0,i); - MarkTileDirty(1,i); - } + return (x >= rect_x && x < rect_x + rect_width && + y >= rect_y && y < rect_y + rect_height); } -void DrawHelpScreenElText(int start) +static void AdjustScrollbar(int id, int items_max, int items_visible, + int item_position) { - int i; - int xstart = SX + 56, ystart = SY + 65 + 2 * 32, ystep = TILEY + 4; - int ybottom = SYSIZE - 20; + struct GadgetInfo *gi = screen_gadget[id]; + + if (item_position > items_max - items_visible) + item_position = items_max - items_visible; + + ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max, + GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible, + GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END); +} + +static void AdjustChooseTreeScrollbar(TreeInfo *ti, int id) +{ + AdjustScrollbar(id, numTreeInfoInGroup(ti), NUM_MENU_ENTRIES_ON_SCREEN, + ti->cl_first); +} + +static void clearMenuListArea(void) +{ + int scrollbar_xpos = mSX + SC_SCROLLBAR_XPOS + menu.scrollbar_xoffset; + + // correct scrollbar position if placed outside menu (playfield) area + if (scrollbar_xpos > SX + SC_SCROLLBAR_XPOS) + scrollbar_xpos = SX + SC_SCROLLBAR_XPOS; + + // clear menu list area, but not title or scrollbar + DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32, + scrollbar_xpos - mSX, NUM_MENU_ENTRIES_ON_SCREEN * 32); - ClearWindow(); - DrawHeadline(); + // special compatibility handling for "Snake Bite" graphics set + if (strPrefix(leveldir_current->identifier, "snake_bite")) + ClearRectangle(drawto, mSX, mSY + MENU_SCREEN_START_YPOS * 32, + scrollbar_xpos - mSX, NUM_MENU_ENTRIES_ON_SCREEN * 32); +} - DrawTextFCentered(100, FC_GREEN, "The game elements:"); +static void drawCursorExt(int xpos, int ypos, boolean active, int graphic) +{ + static int cursor_array[MAX_LEV_FIELDY]; + int x = amSX + TILEX * xpos; + int y = amSY + TILEY * (MENU_SCREEN_START_YPOS + ypos); - for(i=start; i < start + MAX_HELPSCREEN_ELS && i < num_helpscreen_els; i++) + if (xpos == 0) { - DrawText(xstart, - ystart + (i - start) * ystep + (*helpscreen_eltext[i][1] ? 0 : 8), - helpscreen_eltext[i][0], FS_SMALL, FC_YELLOW); - DrawText(xstart, ystart + (i - start) * ystep + 16, - helpscreen_eltext[i][1], FS_SMALL, FC_YELLOW); + if (graphic != -1) + cursor_array[ypos] = graphic; + else + graphic = cursor_array[ypos]; } - DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page"); + if (active) + graphic = BUTTON_ACTIVE(graphic); + + DrawBackgroundForGraphic(x, y, TILEX, TILEY, graphic); + DrawFixedGraphicThruMaskExt(drawto, x, y, graphic, 0); } -void DrawHelpScreenMusicText(int num) +static void initCursor(int ypos, int graphic) { - int ystart = 150, ystep = 30; - int ybottom = SYSIZE - 20; + drawCursorExt(0, ypos, FALSE, graphic); +} - FadeSounds(); - ClearWindow(); - DrawHeadline(); - - DrawTextFCentered(100, FC_GREEN, "The game background music loops:"); - - DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW, - "Excerpt from"); - DrawTextFCentered(ystart + 1 * ystep, FC_RED, "\"%s\"", - helpscreen_music[num][0]); - DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW, - "by"); - DrawTextFCentered(ystart + 3 * ystep, FC_RED, - "%s", helpscreen_music[num][1]); - DrawTextFCentered(ystart + 4 * ystep, FC_YELLOW, - "from the album"); - DrawTextFCentered(ystart + 5 * ystep, FC_RED, "\"%s\"", - helpscreen_music[num][2]); - - DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page"); +static void drawCursor(int ypos, boolean active) +{ + drawCursorExt(0, ypos, active, -1); +} -#if 0 - PlaySoundLoop(background_loop[num]); -#endif +static void drawCursorXY(int xpos, int ypos, int graphic) +{ + drawCursorExt(xpos, ypos, FALSE, graphic); } -void DrawHelpScreenCreditsText() +static void drawChooseTreeCursor(int ypos, boolean active) { - int ystart = 150, ystep = 30; - int ybottom = SYSIZE - 20; + drawCursorExt(0, ypos, active, -1); +} - FadeSounds(); - ClearWindow(); - DrawHeadline(); - - DrawTextFCentered(100, FC_GREEN, - "Credits:"); - DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW, - "DOS port of the game:"); - DrawTextFCentered(ystart + 1 * ystep, FC_RED, - "Guido Schulz"); - DrawTextFCentered(ystart + 2 * ystep, FC_YELLOW, - "Additional toons:"); - DrawTextFCentered(ystart + 3 * ystep, FC_RED, - "Karl Hörnell"); - DrawTextFCentered(ystart + 5 * ystep, FC_YELLOW, - "...and many thanks to all contributors"); - DrawTextFCentered(ystart + 6 * ystep, FC_YELLOW, - "of new levels!"); - - DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for next page"); -} - -void DrawHelpScreenContactText() -{ - int ystart = 150, ystep = 30; - int ybottom = SYSIZE - 20; - - ClearWindow(); - DrawHeadline(); - - DrawTextFCentered(100, FC_GREEN, "Program information:"); - - DrawTextFCentered(ystart + 0 * ystep, FC_YELLOW, - "This game is Freeware!"); - DrawTextFCentered(ystart + 1 * ystep, FC_YELLOW, - "If you like it, send e-mail to:"); - DrawTextFCentered(ystart + 2 * ystep, FC_RED, - "info@artsoft.org"); - DrawTextFCentered(ystart + 3 * ystep, FC_YELLOW, - "or SnailMail to:"); - DrawTextFCentered(ystart + 4 * ystep + 0, FC_RED, - "Holger Schemel"); - DrawTextFCentered(ystart + 4 * ystep + 20, FC_RED, - "Detmolder Strasse 189"); - DrawTextFCentered(ystart + 4 * ystep + 40, FC_RED, - "33604 Bielefeld"); - DrawTextFCentered(ystart + 4 * ystep + 60, FC_RED, - "Germany"); - - DrawTextFCentered(ystart + 7 * ystep, FC_YELLOW, - "If you have created new levels,"); - DrawTextFCentered(ystart + 8 * ystep, FC_YELLOW, - "send them to me to include them!"); - DrawTextFCentered(ystart + 9 * ystep, FC_YELLOW, - ":-)"); - - DrawTextFCentered(ybottom, FC_BLUE, "Press any key or button for main menu"); -} - -void DrawHelpScreen() +static int getChooseTreeEditFont(boolean active) { - int i; + return (active ? FONT_MENU_2_ACTIVE : FONT_MENU_2); +} - UnmapAllGadgets(); - CloseDoor(DOOR_CLOSE_2); +static int getChooseTreeEditXPos(int pos) +{ + boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped; + int xoffset = (has_scrollbar ? -1 : 0); + int xpos = MENU_SCREEN_TEXT2_XPOS + xoffset; + int sx = amSX + xpos * TILEX; + int font_nr = getChooseTreeEditFont(FALSE); + int width = getTextWidth(STR_CHOOSE_TREE_EDIT, font_nr); + + return (pos == POS_RIGHT ? sx + width - 1 : sx); +} - for(i=0;i 1) { - if (helpscreen_state < num_helpscreen_els_pages - 1) - { - for(i=0;iname); + + snprintf(name_cut, max_name_len, "%s", name_full); + snprintf(info_text_title_2, max_text_len, text_format, name_cut); } - BackToFront(); + DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, info_text_title_1); + DrawTextSCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, info_text_title_2); } -void HandleTypeName(int newxpos, Key key) +static void DrawTitleScreenImage(int nr, boolean initial) { - static int xpos = 0, ypos = 2; - - if (newxpos) - { - xpos = newxpos; - DrawText(SX + 6*32, SY + ypos*32, setup.player_name, FS_BIG, FC_YELLOW); - DrawGraphic(xpos + 6, ypos, IMG_BALL_RED, 0); + int graphic = getTitleScreenGraphic(nr, initial); + Bitmap *bitmap = graphic_info[graphic].bitmap; + int width = graphic_info[graphic].width; + int height = graphic_info[graphic].height; + int src_x = graphic_info[graphic].src_x; + int src_y = graphic_info[graphic].src_y; + int dst_x, dst_y; + + if (bitmap == NULL) return; - } - if (((key >= KSYM_A && key <= KSYM_Z) || - (key >= KSYM_a && key <= KSYM_z)) && - xpos < MAX_PLAYER_NAME_LEN) + if (width > WIN_XSIZE) { - char ascii; - - if (key >= KSYM_A && key <= KSYM_Z) - ascii = 'A' + (char)(key - KSYM_A); - else - ascii = 'a' + (char)(key - KSYM_a); - - setup.player_name[xpos] = ascii; - setup.player_name[xpos + 1] = 0; - xpos++; - DrawTextExt(drawto, SX + 6*32, SY + ypos*32, - setup.player_name, FS_BIG, FC_YELLOW); - DrawTextExt(window, SX + 6*32, SY + ypos*32, - setup.player_name, FS_BIG, FC_YELLOW); - DrawGraphic(xpos + 6, ypos, IMG_BALL_RED, 0); + // image width too large for window => center image horizontally + src_x = (width - WIN_XSIZE) / 2; + width = WIN_XSIZE; } - else if ((key == KSYM_Delete || key == KSYM_BackSpace) && xpos > 0) + + if (height > WIN_YSIZE) { - xpos--; - setup.player_name[xpos] = 0; - DrawGraphic(xpos + 6, ypos, IMG_BALL_RED, 0); - DrawGraphic(xpos + 7, ypos, IMG_EMPTY, 0); + // image height too large for window => center image vertically + src_y = (height - WIN_YSIZE) / 2; + height = WIN_YSIZE; } - else if (key == KSYM_Return && xpos > 0) - { - DrawText(SX + 6*32, SY + ypos*32, setup.player_name, FS_BIG, FC_RED); - DrawGraphic(xpos + 6, ypos, IMG_EMPTY, 0); - SaveSetup(); - game_status = MAINMENU; - } + // always display title screens centered + dst_x = (WIN_XSIZE - width) / 2; + dst_y = (WIN_YSIZE - height) / 2; - BackToFront(); -} + SetDrawBackgroundMask(REDRAW_ALL); + SetWindowBackgroundImage(getTitleBackground(nr, initial, TRUE)); -static void DrawChooseTree(TreeInfo **ti_ptr) -{ - UnmapAllGadgets(); - CloseDoor(DOOR_CLOSE_2); + ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE); - ClearWindow(); - HandleChooseTree(0,0, 0,0, MB_MENU_INITIALIZE, ti_ptr); - MapChooseTreeGadgets(*ti_ptr); + if (DrawingOnBackground(dst_x, dst_y)) + BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y); + else + BlitBitmap(bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y); - FadeToFront(); - InitAnimation(); + redraw_mask = REDRAW_ALL; } -static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti) +static void DrawTitleScreenMessage(int nr, boolean initial) { - struct GadgetInfo *gi = screen_gadget[id]; - int items_max, items_visible, item_position; + char *filename = getLevelSetTitleMessageFilename(nr, initial); + struct TitleMessageInfo *tmi = getTitleMessageInfo(nr, initial); - items_max = numTreeInfoInGroup(ti); - items_visible = MAX_MENU_ENTRIES_ON_SCREEN - 1; - item_position = first_entry; + if (filename == NULL) + return; - if (item_position > items_max - items_visible) - item_position = items_max - items_visible; + // force TITLE font on title message screen + SetFontStatus(getTitleMessageGameMode(initial)); - ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max, - GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END); -} + // if chars *and* width set to "-1", automatically determine width + if (tmi->chars == -1 && tmi->width == -1) + tmi->width = viewport.window[game_status].width; -static void drawChooseTreeList(int first_entry, int num_page_entries, - TreeInfo *ti) -{ - int i; - char buffer[SCR_FIELDX * 2]; - int max_buffer_len = (SCR_FIELDX - 2) * 2; - int num_entries = numTreeInfoInGroup(ti); - char *title_string = NULL; - int offset = (ti->type == TREE_TYPE_LEVEL_DIR ? 0 : 16); + // if lines *and* height set to "-1", automatically determine height + if (tmi->lines == -1 && tmi->height == -1) + tmi->height = viewport.window[game_status].height; - ClearRectangle(backbuffer, SX, SY, SXSIZE - 32, SYSIZE); - redraw_mask |= REDRAW_FIELD; + // if chars set to "-1", automatically determine by text and font width + if (tmi->chars == -1) + tmi->chars = tmi->width / getFontWidth(tmi->font); + else + tmi->width = tmi->chars * getFontWidth(tmi->font); - title_string = - (ti->type == TREE_TYPE_LEVEL_DIR ? "Level Directories" : - ti->type == TREE_TYPE_GRAPHICS_DIR ? "Custom Graphics" : - ti->type == TREE_TYPE_SOUNDS_DIR ? "Custom Sounds" : - ti->type == TREE_TYPE_MUSIC_DIR ? "Custom Music" : ""); + // if lines set to "-1", automatically determine by text and font height + if (tmi->lines == -1) + tmi->lines = tmi->height / getFontHeight(tmi->font); + else + tmi->height = tmi->lines * getFontHeight(tmi->font); - DrawText(SX + offset, SY + offset, title_string, FS_BIG, - (ti->type == TREE_TYPE_LEVEL_DIR ? FC_GREEN : FC_YELLOW)); + // if x set to "-1", automatically determine by width and alignment + if (tmi->x == -1) + tmi->x = -1 * ALIGNED_XPOS(0, tmi->width, tmi->align); - for(i=0; iy == -1) + tmi->y = -1 * ALIGNED_YPOS(0, tmi->height, tmi->valign); - node_first = getTreeInfoFirstGroupEntry(ti); - node = getTreeInfoFromPos(node_first, entry_pos); + SetDrawBackgroundMask(REDRAW_ALL); + SetWindowBackgroundImage(getTitleBackground(nr, initial, FALSE)); - strncpy(buffer, node->name , max_buffer_len); - buffer[max_buffer_len] = '\0'; + ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE); - DrawText(SX + 32, SY + ypos * 32, buffer, FS_MEDIUM, node->color); + DrawTextFile(ALIGNED_TEXT_XPOS(tmi), ALIGNED_TEXT_YPOS(tmi), + filename, tmi->font, tmi->chars, -1, tmi->lines, 0, -1, + tmi->autowrap, tmi->centered, tmi->parse_comments); - if (node->parent_link) - initCursor(i, IMG_ARROW_BLUE_LEFT); - else if (node->level_group) - initCursor(i, IMG_ARROW_BLUE_RIGHT); - else - initCursor(i, IMG_BALL_BLUE); - } + ResetFontStatus(); +} - if (first_entry > 0) - DrawGraphic(0, 1, IMG_ARROW_BLUE_UP, 0); +static void DrawTitleScreen(void) +{ + KeyboardAutoRepeatOff(); - if (first_entry + num_page_entries < num_entries) - DrawGraphic(0, MAX_MENU_ENTRIES_ON_SCREEN + 1, IMG_ARROW_BLUE_DOWN, 0); + HandleTitleScreen(0, 0, 0, 0, MB_MENU_INITIALIZE); } -static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti) +static boolean CheckTitleScreen(boolean levelset_has_changed) { - TreeInfo *node, *node_first; - int x, last_redraw_mask = redraw_mask; + static boolean show_title_initial = TRUE; + boolean show_titlescreen = FALSE; - if (ti->type != TREE_TYPE_LEVEL_DIR) - return; + // needed to be able to skip title screen, if no image or message defined + InitializeTitleControls(show_title_initial); - node_first = getTreeInfoFirstGroupEntry(ti); - node = getTreeInfoFromPos(node_first, entry_pos); + if (setup.show_titlescreen && (show_title_initial || levelset_has_changed)) + show_titlescreen = TRUE; - ClearRectangle(drawto, SX + 32, SY + 32, SXSIZE - 64, 32); - - if (node->parent_link) - DrawTextFCentered(40, FC_RED, "leave group \"%s\"", node->class_desc); - else if (node->level_group) - DrawTextFCentered(40, FC_RED, "enter group \"%s\"", node->class_desc); - else if (ti->type == TREE_TYPE_LEVEL_DIR) - DrawTextFCentered(40, FC_RED, "%3d levels (%s)", - node->levels, node->class_desc); + // show initial title images and messages only once at program start + show_title_initial = FALSE; - /* let BackToFront() redraw only what is needed */ - redraw_mask = last_redraw_mask | REDRAW_TILES; - for (x=0; x 0); } -static void HandleChooseTree(int mx, int my, int dx, int dy, int button, - TreeInfo **ti_ptr) +void DrawMainMenu(void) { - static unsigned long choose_delay = 0; - TreeInfo *ti = *ti_ptr; - int x = 0; - int y = ti->cl_cursor; - int step = (button == 1 ? 1 : button == 2 ? 5 : 10); - int num_entries = numTreeInfoInGroup(ti); - int num_page_entries; + static LevelDirTree *leveldir_last_valid = NULL; + boolean levelset_has_changed = FALSE; + int fade_mask = REDRAW_FIELD; - if (num_entries <= MAX_MENU_ENTRIES_ON_SCREEN) - num_page_entries = num_entries; - else - num_page_entries = MAX_MENU_ENTRIES_ON_SCREEN - 1; + LimitScreenUpdates(FALSE); - if (button == MB_MENU_INITIALIZE) + FadeSetLeaveScreen(); + + // do not fade out here -- function may continue and fade on editor screen + + UnmapAllGadgets(); + FadeMenuSoundsAndMusic(); + + ExpireSoundLoops(FALSE); + + KeyboardAutoRepeatOn(); + + audio.sound_deactivated = FALSE; + + GetPlayerConfig(); + + // needed if last screen was the playing screen, invoked from level editor + if (level_editor_test_game) { - int entry_pos = posTreeInfo(ti); + CloseDoor(DOOR_CLOSE_ALL); - if (ti->cl_first == -1) - { - ti->cl_first = MAX(0, entry_pos - num_page_entries + 1); - ti->cl_cursor = - entry_pos - ti->cl_first; - } + SetGameStatus(GAME_MODE_EDITOR); - if (dx == 999) /* first entry is set by scrollbar position */ - ti->cl_first = dy; - else - AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, - ti->cl_first, ti); + DrawLevelEd(); - drawChooseTreeList(ti->cl_first, num_page_entries, ti); - drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti); - drawCursor(ti->cl_cursor, FC_RED); return; } - else if (button == MB_MENU_LEAVE) + + // needed if last screen was the playing screen, invoked from hall of fame + if (score_info_tape_play) { - if (ti->node_parent) - { - *ti_ptr = ti->node_parent; - DrawChooseTree(ti_ptr); - } - else if (game_status == SETUP) - { - execSetupArtwork(); - } - else - { - game_status = MAINMENU; - DrawMainMenu(); - } + CloseDoor(DOOR_CLOSE_ALL); + + SetGameStatus(GAME_MODE_SCOREINFO); + + DrawScoreInfo(scores.last_entry_nr); return; } - if (mx || my) /* mouse input */ + // reset flag to continue playing next level from hall of fame + scores.continue_playing = FALSE; + + // leveldir_current may be invalid (level group, parent link, node copy) + leveldir_current = getValidLevelSeries(leveldir_current, leveldir_last_valid); + + if (leveldir_current != leveldir_last_valid) { - x = (mx - SX) / 32; - y = (my - SY) / 32 - MENU_SCREEN_START_YPOS; - } - else if (dx || dy) /* keyboard input */ - { - if (dy) - y = ti->cl_cursor + dy; - - if (ABS(dy) == SCR_FIELDY) /* handle KSYM_Page_Up, KSYM_Page_Down */ - { - dy = SIGN(dy); - step = num_page_entries - 1; - y = (dy < 0 ? -1 : num_page_entries); - } - } + // level setup config may have been loaded to "last played" tree node copy, + // but "leveldir_current" now points to the "original" level set tree node, + // in which case "handicap_level" may still default to the first level + LoadLevelSetup_SeriesInfo(); - if (x == 0 && y == -1) - { - if (ti->cl_first > 0 && - (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY))) - { - ti->cl_first -= step; - if (ti->cl_first < 0) - ti->cl_first = 0; + UpdateLastPlayedLevels_TreeInfo(); - drawChooseTreeList(ti->cl_first, num_page_entries, ti); - drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti); - drawCursor(ti->cl_cursor, FC_RED); - AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, - ti->cl_first, ti); - return; - } + levelset_has_changed = TRUE; } - else if (x == 0 && y > num_page_entries - 1) - { - if (ti->cl_first + num_page_entries < num_entries && - (dy || DelayReached(&choose_delay, GADGET_FRAME_DELAY))) - { - ti->cl_first += step; - if (ti->cl_first + num_page_entries > num_entries) - ti->cl_first = MAX(0, num_entries - num_page_entries); - drawChooseTreeList(ti->cl_first, num_page_entries, ti); - drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti); - drawCursor(ti->cl_cursor, FC_RED); - AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, - ti->cl_first, ti); - return; - } - } + // store valid level series information + leveldir_last_valid = leveldir_current; - if (dx == 1) + // needed if last screen (level choice) changed graphics, sounds or music + ReloadCustomArtwork(0); + + if (CheckTitleScreen(levelset_has_changed)) { - TreeInfo *node_first, *node_cursor; - int entry_pos = ti->cl_first + y; + SetGameStatus(GAME_MODE_TITLE); - node_first = getTreeInfoFirstGroupEntry(ti); - node_cursor = getTreeInfoFromPos(node_first, entry_pos); + DrawTitleScreen(); - if (node_cursor->node_group) - { - node_cursor->cl_first = ti->cl_first; - node_cursor->cl_cursor = ti->cl_cursor; - *ti_ptr = node_cursor->node_group; - DrawChooseTree(ti_ptr); - return; - } - } - else if (dx == -1 && ti->node_parent) - { - *ti_ptr = ti->node_parent; - DrawChooseTree(ti_ptr); return; } - if (x == 0 && y >= 0 && y < num_page_entries) - { - if (button) - { - if (y != ti->cl_cursor) - { - drawCursor(y, FC_RED); - drawCursor(ti->cl_cursor, FC_BLUE); - drawChooseTreeInfo(ti->cl_first + y, ti); - ti->cl_cursor = y; - } - } - else - { - TreeInfo *node_first, *node_cursor; - int entry_pos = ti->cl_first + y; + if (redraw_mask & REDRAW_ALL) + fade_mask = REDRAW_ALL; - node_first = getTreeInfoFirstGroupEntry(ti); - node_cursor = getTreeInfoFromPos(node_first, entry_pos); + if (CheckFadeAll()) + fade_mask = REDRAW_ALL; - if (node_cursor->node_group) - { - node_cursor->cl_first = ti->cl_first; - node_cursor->cl_cursor = ti->cl_cursor; - *ti_ptr = node_cursor->node_group; - DrawChooseTree(ti_ptr); - } - else if (node_cursor->parent_link) - { - *ti_ptr = node_cursor->node_parent; - DrawChooseTree(ti_ptr); - } - else - { - node_cursor->cl_first = ti->cl_first; - node_cursor->cl_cursor = ti->cl_cursor; - *ti_ptr = node_cursor; + FadeOut(fade_mask); - if (ti->type == TREE_TYPE_LEVEL_DIR) - { - LoadLevelSetup_SeriesInfo(); + // needed if different viewport properties defined for menues + ChangeViewportPropertiesIfNeeded(); - SaveLevelSetup_LastSeries(); - SaveLevelSetup_SeriesInfo(); - TapeErase(); - } + SetDrawtoField(DRAW_TO_BACKBUFFER); - if (game_status == SETUP) - { - execSetupArtwork(); - } - else - { - game_status = MAINMENU; - DrawMainMenu(); - } - } - } - } + // level_nr may have been set to value over handicap with level editor + if (setup.handicap && level_nr > leveldir_current->handicap_level) + level_nr = leveldir_current->handicap_level; + + LoadLevel(level_nr); + LoadScore(level_nr); + + SaveLevelSetup_SeriesInfo(); + + // set this after "ChangeViewportPropertiesIfNeeded()" (which may reset it) + SetDrawDeactivationMask(REDRAW_NONE); + SetDrawBackgroundMask(REDRAW_FIELD); + + SetMainBackgroundImage(IMG_BACKGROUND_MAIN); + +#if 0 + if (fade_mask == REDRAW_ALL) + RedrawGlobalBorder(); +#endif + + ClearField(); + + InitializeMainControls(); + + DrawCursorAndText_Main(-1, FALSE, FALSE); + DrawPreviewLevelInitial(); + DrawNetworkPlayers(); + + HandleMainMenu(0, 0, 0, 0, MB_MENU_INITIALIZE); + + TapeStop(); + if (TAPE_IS_EMPTY(tape)) + LoadTape(level_nr); + DrawCompleteVideoDisplay(); + + PlayMenuSoundsAndMusic(); + + // create gadgets for main menu screen + FreeScreenGadgets(); + CreateScreenGadgets(); + + // may be required if audio buttons shown on tape and changed in setup menu + FreeGameButtons(); + CreateGameButtons(); + + // map gadgets for main menu screen + MapTapeButtons(); + MapScreenMenuGadgets(SCREEN_MASK_MAIN); + UpdateScreenMenuGadgets(SCREEN_MASK_MAIN_HAS_SOLUTION, hasSolutionTape()); + UpdateScreenMenuGadgets(SCREEN_MASK_MAIN_HAS_SET_INFO, hasLevelSetInfo()); + + // copy actual game door content to door double buffer for OpenDoor() + BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0); + BlitBitmap(drawto, bitmap_db_door_2, VX, VY, VXSIZE, VYSIZE, 0, 0); + + OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); + + DrawMaskedBorder(fade_mask); + FadeIn(fade_mask); + FadeSetEnterMenu(); + + // update screen area with special editor door + redraw_mask |= REDRAW_ALL; BackToFront(); - if (game_status == CHOOSELEVEL || game_status == SETUP) - DoAnimation(); -} + SetMouseCursor(CURSOR_DEFAULT); -void DrawChooseLevel() -{ - DrawChooseTree(&leveldir_current); -} + OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2); -void HandleChooseLevel(int mx, int my, int dx, int dy, int button) -{ - HandleChooseTree(mx, my, dx, dy, button, &leveldir_current); + SyncEmscriptenFilesystem(); + + // needed once after program start or after user change + CheckApiServerTasks(); } -void DrawHallOfFame(int highlight_position) +static void gotoTopLevelDir(void) { - UnmapAllGadgets(); - FadeSounds(); - CloseDoor(DOOR_CLOSE_2); + // move upwards until inside (but not above) top level directory + while (leveldir_current->node_parent && + !strEqual(leveldir_current->node_parent->subdir, STRING_TOP_DIRECTORY)) + { + // write a "path" into level tree for easy navigation to last level + if (leveldir_current->node_parent->node_group->cl_first == -1) + { + int num_leveldirs = numTreeInfoInGroup(leveldir_current); + int leveldir_pos = getPosFromTreeInfo(leveldir_current); + int num_page_entries = MIN(num_leveldirs, NUM_MENU_ENTRIES_ON_SCREEN); + int cl_first, cl_cursor; + + cl_first = MAX(0, leveldir_pos - num_page_entries + 1); + cl_cursor = leveldir_pos - cl_first; - if (highlight_position < 0) - LoadScore(level_nr); + leveldir_current->node_parent->node_group->cl_first = cl_first; + leveldir_current->node_parent->node_group->cl_cursor = cl_cursor; + } - FadeToFront(); - InitAnimation(); - HandleHallOfFame(highlight_position,0, 0,0, MB_MENU_INITIALIZE); - PlaySound(SND_MENU_HALL_OF_FAME); + leveldir_current = leveldir_current->node_parent; + } } -static void drawHallOfFameList(int first_entry, int highlight_position) +static unsigned int getAutoDelayCounter(struct TitleFadingInfo *fi) { - int i; + boolean use_frame_counter = (fi->auto_delay_unit == AUTO_DELAY_UNIT_FRAMES); - ClearWindow(); - DrawText(SX + 80, SY + 8, "Hall Of Fame", FS_BIG, FC_YELLOW); - DrawTextFCentered(46, FC_RED, "HighScores of Level %d", level_nr); + return (use_frame_counter ? video.frame_counter : Counter()); +} - for(i=0; iauto_delay, getAutoDelayCounter(fi)); +} -#if 0 - DrawText(SX, SY + 64 + i * 32, ".................", FS_BIG, color); - DrawText(SX, SY + 64 + i * 32, highscore[i].Name, FS_BIG, color); - DrawText(SX + 12 * 32, SY + 64 + i * 32, - int2str(highscore[i].Score, 5), FS_BIG, color); -#else - DrawText(SX, SY + 64 + i * 32, "..................................", - FS_MEDIUM, FC_YELLOW); - DrawText(SX, SY + 64 + i * 32, int2str(entry + 1, 3), - FS_MEDIUM, FC_YELLOW); - DrawText(SX + 64, SY + 64 + i * 32, highscore[entry].Name, FS_BIG, color); - DrawText(SX + 14 * 32 + 16, SY + 64 + i * 32, - int2str(highscore[entry].Score, 5), FS_MEDIUM, color); -#endif - } +static void ResetTitleAutoDelay(unsigned int *counter_var, + struct TitleFadingInfo *fi) +{ + *counter_var = getAutoDelayCounter(fi); } -void HandleHallOfFame(int mx, int my, int dx, int dy, int button) +void HandleTitleScreen(int mx, int my, int dx, int dy, int button) { - static int first_entry = 0; - static int highlight_position = 0; - int step = (button == 1 ? 1 : button == 2 ? 5 : 10); - int button_released = !button; + static unsigned int title_delay = 0; + static int title_screen_nr = 0; + static int last_sound = -1, last_music = -1; + boolean return_to_main_menu = FALSE; + struct TitleControlInfo *tci; + int sound, music; if (button == MB_MENU_INITIALIZE) { - first_entry = 0; - highlight_position = mx; - drawHallOfFameList(first_entry, highlight_position); - return; - } + title_delay = 0; + title_screen_nr = 0; + tci = &title_controls[title_screen_nr]; - if (ABS(dy) == SCR_FIELDY) /* handle KSYM_Page_Up, KSYM_Page_Down */ - step = MAX_MENU_ENTRIES_ON_SCREEN - 1; + SetAnimStatus(getTitleAnimMode(tci)); - if (dy < 0) - { - if (first_entry > 0) + last_sound = SND_UNDEFINED; + last_music = MUS_UNDEFINED; + + if (num_title_screens != 0) { - first_entry -= step; - if (first_entry < 0) - first_entry = 0; + FadeSetEnterScreen(); - drawHallOfFameList(first_entry, highlight_position); - return; + // use individual title fading instead of global "enter screen" fading + fading = getTitleFading(tci); } - } - else if (dy > 0) - { - if (first_entry + MAX_MENU_ENTRIES_ON_SCREEN < MAX_SCORE_ENTRIES) + + if (game_status_last_screen == GAME_MODE_INFO) { - first_entry += step; - if (first_entry + MAX_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES) - first_entry = MAX(0, MAX_SCORE_ENTRIES - MAX_MENU_ENTRIES_ON_SCREEN); + if (num_title_screens == 0) + { + // switch game mode from title screen mode back to info screen mode + SetGameStatus(GAME_MODE_INFO); - drawHallOfFameList(first_entry, highlight_position); - return; + // store that last screen was info screen, not main menu screen + game_status_last_screen = GAME_MODE_INFO; + + DrawInfoScreen_NotAvailable("Title screen information:", + "No title screen for this level set."); + return; + } } + + FadeMenuSoundsAndMusic(); + + FadeOut(REDRAW_ALL); + + // title screens may have different window size + ChangeViewportPropertiesIfNeeded(); + + // only required to update logic for redrawing global border + ClearField(); + + if (tci->is_image) + DrawTitleScreenImage(tci->local_nr, tci->initial); + else + DrawTitleScreenMessage(tci->local_nr, tci->initial); + + sound = getTitleSound(tci); + music = getTitleMusic(tci); + + if (sound != last_sound) + PlayMenuSoundExt(sound); + if (music != last_music) + PlayMenuMusicExt(music); + + last_sound = sound; + last_music = music; + + SetMouseCursor(CURSOR_NONE); + + FadeIn(REDRAW_ALL); + + ResetTitleAutoDelay(&title_delay, &fading); + + return; } - if (button_released) + if (fading.auto_delay > 0 && TitleAutoDelayReached(&title_delay, &fading)) + button = MB_MENU_CHOICE; + + if (button == MB_MENU_LEAVE) { - FadeSound(SND_MENU_HALL_OF_FAME); - game_status = MAINMENU; - DrawMainMenu(); + return_to_main_menu = TRUE; } + else if (button == MB_MENU_CHOICE || dx) + { + if (game_status_last_screen == GAME_MODE_INFO && num_title_screens == 0) + { + SetGameStatus(GAME_MODE_INFO); - BackToFront(); + info_mode = INFO_MODE_MAIN; - if (game_status == HALLOFFAME) - DoAnimation(); -} + DrawInfoScreen(); + return; + } -/* ========================================================================= */ -/* setup screen functions */ -/* ========================================================================= */ + title_screen_nr += + (game_status_last_screen == GAME_MODE_INFO && dx < 0 ? -1 : +1); -static struct TokenInfo *setup_info; -static int num_setup_info; + if (title_screen_nr >= 0 && title_screen_nr < num_title_screens) + { + tci = &title_controls[title_screen_nr]; -static char *graphics_set_name; -static char *sounds_set_name; -static char *music_set_name; + SetAnimStatus(getTitleAnimMode(tci)); -static void execSetupMain() -{ - setup_mode = SETUP_MODE_MAIN; - DrawSetupScreen(); -} + sound = getTitleSound(tci); + music = getTitleMusic(tci); -static void execSetupGame() -{ - setup_mode = SETUP_MODE_GAME; - DrawSetupScreen(); -} + if (last_sound != SND_UNDEFINED && sound != last_sound) + FadeSound(last_sound); + if (last_music != MUS_UNDEFINED && music != last_music) + FadeMusic(); -static void execSetupGraphics() -{ - setup_mode = SETUP_MODE_GRAPHICS; - DrawSetupScreen(); -} + fading = getTitleFading(tci); -static void execSetupSound() -{ - setup_mode = SETUP_MODE_SOUND; - DrawSetupScreen(); -} + FadeOut(REDRAW_ALL); -static void execSetupArtwork() -{ - /* needed if last screen (setup choice) changed graphics, sounds or music */ - ReloadCustomArtwork(); + if (tci->is_image) + DrawTitleScreenImage(tci->local_nr, tci->initial); + else + DrawTitleScreenMessage(tci->local_nr, tci->initial); - setup.graphics_set = artwork.gfx_current->identifier; - setup.sounds_set = artwork.snd_current->identifier; - setup.music_set = artwork.mus_current->identifier; + sound = getTitleSound(tci); + music = getTitleMusic(tci); - /* needed for displaying artwork name instead of artwork identifier */ - graphics_set_name = artwork.gfx_current->name; - sounds_set_name = artwork.snd_current->name; - music_set_name = artwork.mus_current->name; + if (sound != last_sound) + PlayMenuSoundExt(sound); + if (music != last_music) + PlayMenuMusicExt(music); - setup_mode = SETUP_MODE_ARTWORK; - DrawSetupScreen(); -} + last_sound = sound; + last_music = music; -static void execSetupChooseGraphics() -{ - setup_mode = SETUP_MODE_CHOOSE_GRAPHICS; - DrawSetupScreen(); -} + FadeIn(REDRAW_ALL); -static void execSetupChooseSounds() -{ - setup_mode = SETUP_MODE_CHOOSE_SOUNDS; - DrawSetupScreen(); -} + ResetTitleAutoDelay(&title_delay, &fading); + } + else + { + FadeMenuSoundsAndMusic(); -static void execSetupChooseMusic() -{ - setup_mode = SETUP_MODE_CHOOSE_MUSIC; - DrawSetupScreen(); -} + return_to_main_menu = TRUE; + } + } -static void execSetupInput() -{ - setup_mode = SETUP_MODE_INPUT; - DrawSetupScreen(); -} + if (return_to_main_menu) + { + SetMouseCursor(CURSOR_DEFAULT); -static void execSetupShortcut() -{ - setup_mode = SETUP_MODE_SHORTCUT; - DrawSetupScreen(); -} + // force full menu screen redraw after displaying title screens + redraw_mask = REDRAW_ALL; -static void execExitSetup() -{ - game_status = MAINMENU; - DrawMainMenu(); + if (game_status_last_screen == GAME_MODE_INFO) + { + SetGameStatus(GAME_MODE_INFO); + + info_mode = INFO_MODE_MAIN; + + DrawInfoScreen(); + } + else // default: return to main menu + { + SetGameStatus(GAME_MODE_MAIN); + + DrawMainMenu(); + } + } } -static void execSaveAndExitSetup() +static void HandleMainMenu_ToggleTeamMode(void) { - SaveSetup(); - execExitSetup(); + setup.team_mode = !setup.team_mode; + + InitializeMainControls(); + DrawCursorAndText_Main(MAIN_CONTROL_NAME, TRUE, FALSE); + + DrawPreviewPlayers(); } -static struct TokenInfo setup_info_main[] = +static void HandleMainMenu_SelectLevel(int step, int direction, + int selected_level_nr) { - { TYPE_ENTER_MENU, execSetupGame, "Game Settings" }, - { TYPE_ENTER_MENU, execSetupGraphics, "Graphics" }, - { TYPE_ENTER_MENU, execSetupSound, "Sound & Music" }, - { TYPE_ENTER_MENU, execSetupArtwork, "Custom Artwork" }, - { TYPE_ENTER_MENU, execSetupInput, "Input Devices" }, - { TYPE_ENTER_MENU, execSetupShortcut, "Key Shortcuts" }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_LEAVE_MENU, execExitSetup, "Exit" }, - { TYPE_LEAVE_MENU, execSaveAndExitSetup, "Save and exit" }, - { 0, NULL, NULL } -}; - -static struct TokenInfo setup_info_game[] = -{ - { TYPE_SWITCH, &setup.team_mode, "Team-Mode:" }, - { TYPE_SWITCH, &setup.handicap, "Handicap:" }, - { TYPE_SWITCH, &setup.time_limit, "Timelimit:" }, - { TYPE_SWITCH, &setup.autorecord, "Auto-Record:" }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_LEAVE_MENU, execSetupMain, "Exit" }, - { 0, NULL, NULL } -}; - -static struct TokenInfo setup_info_graphics[] = -{ - { TYPE_SWITCH, &setup.fullscreen, "Fullscreen:" }, - { TYPE_SWITCH, &setup.scroll_delay, "Scroll Delay:" }, - { TYPE_SWITCH, &setup.soft_scrolling, "Soft Scroll.:" }, -#if 0 - { TYPE_SWITCH, &setup.double_buffering,"Buffered gfx:" }, - { TYPE_SWITCH, &setup.fading, "Fading:" }, -#endif - { TYPE_SWITCH, &setup.quick_doors, "Quick Doors:" }, - { TYPE_SWITCH, &setup.toons, "Toons:" }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_LEAVE_MENU, execSetupMain, "Exit" }, - { 0, NULL, NULL } -}; + int old_level_nr = level_nr; + int new_level_nr; -static struct TokenInfo setup_info_sound[] = -{ - { TYPE_SWITCH, &setup.sound, "Sound:", }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_SWITCH, &setup.sound_simple, "Simple Sound:" }, - { TYPE_SWITCH, &setup.sound_loops, "Sound Loops:" }, - { TYPE_SWITCH, &setup.sound_music, "Game Music:" }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_LEAVE_MENU, execSetupMain, "Exit" }, - { 0, NULL, NULL } -}; + if (selected_level_nr != NO_DIRECT_LEVEL_SELECT) + new_level_nr = selected_level_nr; + else + new_level_nr = old_level_nr + step * direction; -static struct TokenInfo setup_info_artwork[] = -{ - { TYPE_ENTER_MENU, execSetupChooseGraphics,"Custom Graphics" }, - { TYPE_STRING, &graphics_set_name, "" }, - { TYPE_ENTER_MENU, execSetupChooseSounds, "Custom Sounds" }, - { TYPE_STRING, &sounds_set_name, "" }, - { TYPE_ENTER_MENU, execSetupChooseMusic, "Custom Music" }, - { TYPE_STRING, &music_set_name, "" }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_STRING, NULL, "Override Level Artwork:"}, - { TYPE_YES_NO, &setup.override_level_graphics, "Graphics:" }, - { TYPE_YES_NO, &setup.override_level_sounds, "Sounds:" }, - { TYPE_YES_NO, &setup.override_level_music, "Music:" }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_LEAVE_MENU, execSetupMain, "Exit" }, - { 0, NULL, NULL } -}; + if (new_level_nr < leveldir_current->first_level) + new_level_nr = leveldir_current->first_level; + if (new_level_nr > leveldir_current->last_level) + new_level_nr = leveldir_current->last_level; -static struct TokenInfo setup_info_shortcut[] = -{ - { TYPE_KEYTEXT, NULL, "Quick Save Game:", }, - { TYPE_KEY, &setup.shortcut.save_game, "" }, - { TYPE_KEYTEXT, NULL, "Quick Load Game:", }, - { TYPE_KEY, &setup.shortcut.load_game, "" }, - { TYPE_KEYTEXT, NULL, "Toggle Pause:", }, - { TYPE_KEY, &setup.shortcut.toggle_pause, "" }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_YES_NO, &setup.ask_on_escape, "Ask on Esc:" }, - { TYPE_EMPTY, NULL, "" }, - { TYPE_LEAVE_MENU, execSetupMain, "Exit" }, - { 0, NULL, NULL } -}; + if (setup.handicap && new_level_nr > leveldir_current->handicap_level) + { + // skipping levels is only allowed when trying to skip single level + if (setup.skip_levels && new_level_nr == old_level_nr + 1 && + Request("Level still unsolved! Skip it anyway?", REQ_ASK)) + { + leveldir_current->handicap_level++; + SaveLevelSetup_SeriesInfo(); + } -static Key getSetupKey() -{ - Key key = KSYM_UNDEFINED; - boolean got_key_event = FALSE; + new_level_nr = leveldir_current->handicap_level; + } - while (!got_key_event) + if (new_level_nr != old_level_nr) { - if (PendingEvent()) /* got event */ - { - Event event; + struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_LEVEL_NUMBER); - NextEvent(&event); + PlaySound(SND_MENU_ITEM_SELECTING); - switch(event.type) - { - case EVENT_KEYPRESS: - { - key = GetEventKey((KeyEvent *)&event, TRUE); + level_nr = new_level_nr; - /* press 'Escape' or 'Enter' to keep the existing key binding */ - if (key == KSYM_Escape || key == KSYM_Return) - key = KSYM_UNDEFINED; /* keep old value */ + DrawText(mSX + mci->pos_text->x, mSY + mci->pos_text->y, + int2str(level_nr, menu.main.text.level_number.size), + mci->pos_text->font); - got_key_event = TRUE; - } - break; + LoadLevel(level_nr); + DrawPreviewLevelInitial(); - case EVENT_KEYRELEASE: - key_joystick_mapping = 0; - break; + TapeErase(); + LoadTape(level_nr); + DrawCompleteVideoDisplay(); - default: - HandleOtherEvents(&event); - break; - } - } + SaveLevelSetup_SeriesInfo(); - BackToFront(); - DoAnimation(); + UpdateScreenMenuGadgets(SCREEN_MASK_MAIN_HAS_SOLUTION, hasSolutionTape()); - /* don't eat all CPU time */ - Delay(10); + // force redraw of playfield area (may be reset at this point) + redraw_mask |= REDRAW_FIELD; } - - return key; } -static void drawSetupValue(int pos) +void HandleMainMenu(int mx, int my, int dx, int dy, int button) { - int xpos = MENU_SCREEN_VALUE_XPOS; - int ypos = MENU_SCREEN_START_YPOS + pos; - int font_size = FS_BIG; - int font_color = FC_YELLOW; - char *value_string = getSetupValue(setup_info[pos].type & ~TYPE_GHOSTED, - setup_info[pos].value); - - if (value_string == NULL) - return; - - if (setup_info[pos].type & TYPE_KEY) - { - xpos = 3; + static int choice = MAIN_CONTROL_GAME; + static boolean button_pressed_last = FALSE; + boolean button_pressed = FALSE; + int pos = choice; + int i = 0; // needed to prevent compiler warning due to bad code below - if (setup_info[pos].type & TYPE_QUERY) - { - value_string = ""; - font_color = FC_RED; - } - } - else if (setup_info[pos].type & TYPE_STRING) + if (button == MB_MENU_INITIALIZE) { - int max_value_len = (SCR_FIELDX - 2) * 2; + DrawCursorAndText_Main(choice, TRUE, FALSE); - xpos = 1; - font_size = FS_MEDIUM; - - if (strlen(value_string) > max_value_len) - value_string[max_value_len] = '\0'; + return; } - else if (setup_info[pos].type & TYPE_BOOLEAN_STYLE && - !*(boolean *)(setup_info[pos].value)) - font_color = FC_BLUE; - DrawText(SX + xpos * 32, SY + ypos * 32, - (xpos == 3 ? " " : " "), FS_BIG, FC_YELLOW); - DrawText(SX + xpos * 32, SY + ypos * 32, value_string, font_size,font_color); -} - -static void changeSetupValue(int pos) -{ - if (setup_info[pos].type & TYPE_BOOLEAN_STYLE) - { - *(boolean *)setup_info[pos].value ^= TRUE; - } - else if (setup_info[pos].type & TYPE_KEY) + if (mx || my) // mouse input { - Key key; + pos = -1; - setup_info[pos].type |= TYPE_QUERY; - drawSetupValue(pos); - setup_info[pos].type &= ~TYPE_QUERY; + for (i = 0; main_controls[i].nr != -1; i++) + { + if (insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY) || + insideTextPosRect(main_controls[i].pos_text, mx - mSX, my - mSY) || + insideTextPosRect(main_controls[i].pos_input, mx - mSX, my - mSY)) + { + pos = main_controls[i].nr; - key = getSetupKey(); - if (key != KSYM_UNDEFINED) - *(Key *)setup_info[pos].value = key; - } + break; + } + } - drawSetupValue(pos); -} + // check if level preview was clicked + if (insidePreviewRect(&preview, mx - SX, my - SY)) + pos = MAIN_CONTROL_GAME; -static void DrawSetupScreen_Generic() -{ - char *title_string = NULL; - int i; + // handle pressed/unpressed state for active/inactive menu buttons + // (if pos != -1, "i" contains index position corresponding to "pos") + if (button && + pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT && + insideMenuPosRect(main_controls[i].pos_button, mx - mSX, my - mSY)) + button_pressed = TRUE; - UnmapAllGadgets(); - CloseDoor(DOOR_CLOSE_2); - ClearWindow(); + if (button_pressed != button_pressed_last) + { + DrawCursorAndText_Main(choice, TRUE, button_pressed); - if (setup_mode == SETUP_MODE_MAIN) - { - setup_info = setup_info_main; - title_string = "Setup"; + if (button_pressed) + PlaySound(SND_MENU_BUTTON_PRESSING); + else + PlaySound(SND_MENU_BUTTON_RELEASING); + } } - else if (setup_mode == SETUP_MODE_GAME) + else if (dx || dy) // keyboard input { - setup_info = setup_info_game; - title_string = "Setup Game"; + if (dx > 0 && (choice == MAIN_CONTROL_INFO || + choice == MAIN_CONTROL_SETUP)) + button = MB_MENU_CHOICE; + else if (dy) + pos = choice + dy; } - else if (setup_mode == SETUP_MODE_GRAPHICS) + + if (pos == MAIN_CONTROL_FIRST_LEVEL && !button) { - setup_info = setup_info_graphics; - title_string = "Setup Graphics"; + HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT); } - else if (setup_mode == SETUP_MODE_SOUND) + else if (pos == MAIN_CONTROL_LAST_LEVEL && !button) { - setup_info = setup_info_sound; - title_string = "Setup Sound"; + HandleMainMenu_SelectLevel(MAX_LEVELS, +1, NO_DIRECT_LEVEL_SELECT); } - else if (setup_mode == SETUP_MODE_ARTWORK) + else if (pos == MAIN_CONTROL_LEVEL_NUMBER && !button) { - setup_info = setup_info_artwork; - title_string = "Custom Artwork"; + CloseDoor(DOOR_CLOSE_2); + + SetGameStatus(GAME_MODE_LEVELNR); + + DrawChooseLevelNr(); } - else if (setup_mode == SETUP_MODE_SHORTCUT) + else if (pos >= MAIN_CONTROL_NAME && pos <= MAIN_CONTROL_QUIT) { - setup_info = setup_info_shortcut; - title_string = "Setup Shortcuts"; - } + if (button) + { + if (pos != choice) + { + PlaySound(SND_MENU_ITEM_ACTIVATING); - DrawText(SX + 16, SY + 16, title_string, FS_BIG, FC_YELLOW); + DrawCursorAndText_Main(choice, FALSE, FALSE); + DrawCursorAndText_Main(pos, TRUE, button_pressed); - num_setup_info = 0; - for(i=0; setup_info[i].type != 0 && i < MAX_MENU_ENTRIES_ON_SCREEN; i++) - { - void *value_ptr = setup_info[i].value; - int ypos = MENU_SCREEN_START_YPOS + i; - int font_size = FS_BIG; + choice = pos; + } + else if (dx != 0) + { + if (choice == MAIN_CONTROL_NAME) + { + // special case: cursor left or right pressed -- toggle team mode + HandleMainMenu_ToggleTeamMode(); + } + else if (choice != MAIN_CONTROL_INFO && + choice != MAIN_CONTROL_SETUP) + { + HandleMainMenu_SelectLevel(1, dx, NO_DIRECT_LEVEL_SELECT); + } + } + } + else + { + PlaySound(SND_MENU_ITEM_SELECTING); - /* set some entries to "unchangeable" according to other variables */ - if ((value_ptr == &setup.sound && !audio.sound_available) || - (value_ptr == &setup.sound_loops && !audio.loops_available) || - (value_ptr == &setup.sound_music && !audio.music_available) || - (value_ptr == &setup.fullscreen && !video.fullscreen_available)) - setup_info[i].type |= TYPE_GHOSTED; + if (pos == MAIN_CONTROL_NAME) + { + if ((mx || my) && + insideTextPosRect(main_controls[i].pos_text, mx - mSX, my - mSY)) + { + // special case: menu text "name/team" clicked -- toggle team mode + HandleMainMenu_ToggleTeamMode(); + } + else + { + if (setup.multiple_users) + { + CloseDoor(DOOR_CLOSE_2); - if (setup_info[i].type & TYPE_STRING) - font_size = FS_MEDIUM; + SetGameStatus(GAME_MODE_NAMES); - DrawText(SX + 32, SY + ypos * 32, setup_info[i].text, font_size, FC_GREEN); + DrawChoosePlayerName(); + } + else + { + SetGameStatus(GAME_MODE_PSEUDO_TYPENAME); - if (setup_info[i].type & TYPE_ENTER_MENU) - initCursor(i, IMG_ARROW_BLUE_RIGHT); - else if (setup_info[i].type & TYPE_LEAVE_MENU) - initCursor(i, IMG_ARROW_BLUE_LEFT); - else if (setup_info[i].type & ~TYPE_SKIP_ENTRY) - initCursor(i, IMG_BALL_BLUE); + DrawTypeName(); + } + } + } + else if (pos == MAIN_CONTROL_LEVELS) + { + if (leveldir_first) + { + CloseDoor(DOOR_CLOSE_2); - if (setup_info[i].type & TYPE_VALUE) - drawSetupValue(i); + SetGameStatus(GAME_MODE_LEVELS); - num_setup_info++; - } + SaveLevelSetup_LastSeries(); + SaveLevelSetup_SeriesInfo(); - FadeToFront(); - InitAnimation(); - HandleSetupScreen_Generic(0,0,0,0,MB_MENU_INITIALIZE); -} + // restore level set if chosen from "last played level set" menu + RestoreLastPlayedLevels(&leveldir_current); -void HandleSetupScreen_Generic(int mx, int my, int dx, int dy, int button) -{ - static int choice_store[MAX_SETUP_MODES]; - int choice = choice_store[setup_mode]; - int x = 0; - int y = choice; + if (setup.internal.choose_from_top_leveldir) + gotoTopLevelDir(); - if (button == MB_MENU_INITIALIZE) - { - drawCursor(choice, FC_RED); - return; - } - else if (button == MB_MENU_LEAVE) - { - for (y=0; yreadonly && + setup.editor.show_read_only_warning) + Request("This level is read-only!", REQ_CONFIRM | REQ_STAY_OPEN); - return; - } + CloseDoor(DOOR_CLOSE_ALL); - if (mx || my) /* mouse input */ - { - x = (mx - SX) / 32; - y = (my - SY) / 32 - MENU_SCREEN_START_YPOS; - } - else if (dx || dy) /* keyboard input */ - { - if (dx) - { - int menu_navigation_type = (dx < 0 ? TYPE_LEAVE_MENU : TYPE_ENTER_MENU); + SetGameStatus(GAME_MODE_EDITOR); - if ((setup_info[choice].type & menu_navigation_type) || - (setup_info[choice].type & TYPE_BOOLEAN_STYLE)) - button = MB_MENU_CHOICE; - } - else if (dy) - y = choice + dy; + FadeSetEnterScreen(); - /* jump to next non-empty menu entry (up or down) */ - while (y > 0 && y < num_setup_info - 1 && - (setup_info[y].type & TYPE_SKIP_ENTRY)) - y += dy; - } + DrawLevelEd(); + } + else if (pos == MAIN_CONTROL_INFO) + { + CloseDoor(DOOR_CLOSE_2); - if (x == 0 && y >= 0 && y < num_setup_info && - (setup_info[y].type & ~TYPE_SKIP_ENTRY)) - { - if (button) - { - if (y != choice) + SetGameStatus(GAME_MODE_INFO); + + info_mode = INFO_MODE_MAIN; + + DrawInfoScreen(); + } + else if (pos == MAIN_CONTROL_GAME) { - drawCursor(y, FC_RED); - drawCursor(choice, FC_BLUE); - choice = choice_store[setup_mode] = y; + StartGameActions(network.enabled, setup.autorecord, level.random_seed); } - } - else if (!(setup_info[y].type & TYPE_GHOSTED)) - { - if (setup_info[y].type & TYPE_ENTER_OR_LEAVE_MENU) + else if (pos == MAIN_CONTROL_SETUP) { - void (*menu_callback_function)(void) = setup_info[choice].value; + CloseDoor(DOOR_CLOSE_2); - menu_callback_function(); + SetGameStatus(GAME_MODE_SETUP); + + setup_mode = SETUP_MODE_MAIN; + + DrawSetupScreen(); } - else + else if (pos == MAIN_CONTROL_QUIT) { - if ((setup_info[y].type & TYPE_KEYTEXT) && - (setup_info[y + 1].type & TYPE_KEY)) - y++; + SaveLevelSetup_LastSeries(); + SaveLevelSetup_SeriesInfo(); - if (setup_info[y].type & TYPE_VALUE) - changeSetupValue(y); +#if defined(PLATFORM_EMSCRIPTEN) + Request("Close the browser window to quit!", REQ_CONFIRM); +#else + if (!setup.ask_on_quit_program || + Request("Do you really want to quit?", REQ_ASK | REQ_STAY_CLOSED)) + SetGameStatus(GAME_MODE_QUIT); +#endif } } } - BackToFront(); - - if (game_status == SETUP) - DoAnimation(); + button_pressed_last = button_pressed; } -void DrawSetupScreen_Input() -{ - ClearWindow(); - DrawText(SX+16, SY+16, "Setup Input", FS_BIG, FC_YELLOW); - - initCursor(0, IMG_BALL_BLUE); - initCursor(1, IMG_BALL_BLUE); - initCursor(2, IMG_ARROW_BLUE_RIGHT); - initCursor(13, IMG_ARROW_BLUE_LEFT); - DrawGraphic(10, MENU_SCREEN_START_YPOS, IMG_ARROW_BLUE_LEFT, 0); - DrawGraphic(12, MENU_SCREEN_START_YPOS, IMG_ARROW_BLUE_RIGHT, 0); +// ============================================================================ +// info screen functions +// ============================================================================ - DrawText(SX+32, SY+2*32, "Player:", FS_BIG, FC_GREEN); - DrawText(SX+32, SY+3*32, "Device:", FS_BIG, FC_GREEN); - DrawText(SX+32, SY+15*32, "Exit", FS_BIG, FC_GREEN); +static struct TokenInfo *info_info; +static int num_info_info; // number of info entries shown on screen +static int max_info_info; // total number of info entries in list -#if 0 - DeactivateJoystickForCalibration(); - DrawTextFCentered(SYSIZE - 20, FC_BLUE, - "Joysticks deactivated on this screen"); -#endif +static void execInfoTitleScreen(void) +{ + info_mode = INFO_MODE_TITLE; - HandleSetupScreen_Input(0,0, 0,0, MB_MENU_INITIALIZE); - FadeToFront(); - InitAnimation(); + DrawInfoScreen(); } -static void setJoystickDeviceToNr(char *device_name, int device_nr) +static void execInfoElements(void) { - if (device_name == NULL) - return; + info_mode = INFO_MODE_ELEMENTS; - if (device_nr < 0 || device_nr >= MAX_PLAYERS) - device_nr = 0; + DrawInfoScreen(); +} - if (strlen(device_name) > 1) - { - char c1 = device_name[strlen(device_name) - 1]; - char c2 = device_name[strlen(device_name) - 2]; +static void execInfoMusic(void) +{ + info_mode = INFO_MODE_MUSIC; - if (c1 >= '0' && c1 <= '9' && !(c2 >= '0' && c2 <= '9')) - device_name[strlen(device_name) - 1] = '0' + (char)(device_nr % 10); - } - else - strncpy(device_name, getDeviceNameFromJoystickNr(device_nr), - strlen(device_name)); + DrawInfoScreen(); } -static void drawPlayerSetupInputInfo(int player_nr) +static void execInfoCredits(void) { - int i; - static struct SetupKeyboardInfo custom_key; - static struct - { - Key *key; - char *text; - } custom[] = - { - { &custom_key.left, "Joystick Left" }, - { &custom_key.right, "Joystick Right" }, - { &custom_key.up, "Joystick Up" }, - { &custom_key.down, "Joystick Down" }, - { &custom_key.snap, "Button 1" }, - { &custom_key.bomb, "Button 2" } - }; - static char *joystick_name[MAX_PLAYERS] = - { - "Joystick1", - "Joystick2", - "Joystick3", - "Joystick4" - }; + info_mode = INFO_MODE_CREDITS; - custom_key = setup.input[player_nr].key; + DrawInfoScreen(); +} - DrawText(SX+11*32, SY+2*32, int2str(player_nr + 1, 1), FS_BIG, FC_RED); - DrawGraphic(8, 2, PLAYER_NR_GFX(IMG_PLAYER1, player_nr), 0); +static void execInfoProgram(void) +{ + info_mode = INFO_MODE_PROGRAM; - if (setup.input[player_nr].use_joystick) - { - char *device_name = setup.input[player_nr].joy.device_name; + DrawInfoScreen(); +} - DrawText(SX+8*32, SY+3*32, - joystick_name[getJoystickNrFromDeviceName(device_name)], - FS_BIG, FC_YELLOW); - DrawText(SX+32, SY+4*32, "Calibrate", FS_BIG, FC_GREEN); - } +static void execInfoVersion(void) +{ + info_mode = INFO_MODE_VERSION; + + DrawInfoScreen(); +} + +static void execInfoLevelSet(void) +{ + info_mode = INFO_MODE_LEVELSET; + + DrawInfoScreen(); +} + +static void execExitInfo(void) +{ + SetGameStatus(GAME_MODE_MAIN); + + DrawMainMenu(); +} + +static struct TokenInfo info_info_main[] = +{ + { TYPE_ENTER_SCREEN, execInfoTitleScreen, STR_INFO_TITLE }, + { TYPE_ENTER_SCREEN, execInfoElements, STR_INFO_ELEMENTS }, + { TYPE_ENTER_SCREEN, execInfoMusic, STR_INFO_MUSIC }, + { TYPE_ENTER_SCREEN, execInfoCredits, STR_INFO_CREDITS }, + { TYPE_ENTER_SCREEN, execInfoProgram, STR_INFO_PROGRAM }, + { TYPE_ENTER_SCREEN, execInfoVersion, STR_INFO_VERSION }, + { TYPE_ENTER_SCREEN, execInfoLevelSet, STR_INFO_LEVELSET }, + { TYPE_EMPTY, NULL, "" }, + { TYPE_LEAVE_MENU, execExitInfo, STR_INFO_EXIT }, + + { 0, NULL, NULL } +}; + +static int getMenuTextFont(int type) +{ + if (type & (TYPE_SWITCH | + TYPE_YES_NO | + TYPE_YES_NO_AUTO | + TYPE_STRING | + TYPE_PLAYER | + TYPE_ECS_AGA | + TYPE_KEYTEXT | + TYPE_ENTER_LIST | + TYPE_TEXT_INPUT)) + return FONT_MENU_2; else - { - DrawText(SX+8*32, SY+3*32, "Keyboard ", FS_BIG, FC_YELLOW); - DrawText(SX+32, SY+4*32, "Customize", FS_BIG, FC_GREEN); - } + return FONT_MENU_1; +} - DrawText(SX+32, SY+5*32, "Actual Settings:", FS_BIG, FC_GREEN); - DrawGraphic(1, 6, IMG_ARROW_BLUE_LEFT, 0); - DrawGraphic(1, 7, IMG_ARROW_BLUE_RIGHT, 0); - DrawGraphic(1, 8, IMG_ARROW_BLUE_UP, 0); - DrawGraphic(1, 9, IMG_ARROW_BLUE_DOWN, 0); - DrawText(SX+2*32, SY+6*32, ":", FS_BIG, FC_BLUE); - DrawText(SX+2*32, SY+7*32, ":", FS_BIG, FC_BLUE); - DrawText(SX+2*32, SY+8*32, ":", FS_BIG, FC_BLUE); - DrawText(SX+2*32, SY+9*32, ":", FS_BIG, FC_BLUE); - DrawText(SX+32, SY+10*32, "Snap Field:", FS_BIG, FC_BLUE); - DrawText(SX+32, SY+12*32, "Place Bomb:", FS_BIG, FC_BLUE); +static struct TokenInfo *setup_info; +static struct TokenInfo setup_info_input[]; - for (i=0; i<6; i++) - { - int ypos = 6 + i + (i > 3 ? i-3 : 0); +static struct TokenInfo *menu_info; - DrawText(SX + 3*32, SY + ypos*32, - " ", FS_BIG, FC_YELLOW); - DrawText(SX + 3*32, SY + ypos*32, - (setup.input[player_nr].use_joystick ? - custom[i].text : - getKeyNameFromKey(*custom[i].key)), - FS_BIG, FC_YELLOW); - } +static void PlayInfoSound(void) +{ + int info_sound = getInfoScreenBackgroundSound_Generic(); + char *next_sound = getSoundInfoEntryFilename(info_sound); + + if (next_sound != NULL) + PlayMenuSoundExt(info_sound); + else + PlayMenuSound(); } -void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button) +static void PlayInfoSoundIfLoop(void) { - static int choice = 0; - static int player_nr = 0; - int x = 0; - int y = choice; - int pos_start = SETUPINPUT_SCREEN_POS_START; - int pos_empty1 = SETUPINPUT_SCREEN_POS_EMPTY1; - int pos_empty2 = SETUPINPUT_SCREEN_POS_EMPTY2; - int pos_end = SETUPINPUT_SCREEN_POS_END; + int info_sound = getInfoScreenBackgroundSound_Generic(); + char *next_sound = getSoundInfoEntryFilename(info_sound); - if (button == MB_MENU_INITIALIZE) + if (next_sound != NULL) + PlayMenuSoundIfLoopExt(info_sound); + else + PlayMenuSoundIfLoop(); +} + +static void PlayInfoMusic(void) +{ + int info_music = getInfoScreenBackgroundMusic_Generic(); + char *curr_music = getCurrentlyPlayingMusicFilename(); + char *next_music = getMusicInfoEntryFilename(info_music); + + if (next_music != NULL) { - drawPlayerSetupInputInfo(player_nr); - drawCursor(choice, FC_RED); - return; + // play music if info screen music differs from current music + if (!strEqual(curr_music, next_music)) + PlayMenuMusicExt(info_music); } - else if (button == MB_MENU_LEAVE) + else { - setup_mode = SETUP_MODE_MAIN; - DrawSetupScreen(); - InitJoysticks(); + // only needed if info screen was directly invoked from main menu + PlayMenuMusic(); } +} + +static void PlayInfoSoundsAndMusic(void) +{ + PlayInfoSound(); + PlayInfoMusic(); +} + +static void FadeInfoSounds(void) +{ + FadeSounds(); +} + +static void FadeInfoMusic(void) +{ + int info_music = getInfoScreenBackgroundMusic_Generic(); + char *curr_music = getCurrentlyPlayingMusicFilename(); + char *next_music = getMusicInfoEntryFilename(info_music); - if (mx || my) /* mouse input */ + if (next_music != NULL) { - x = (mx - SX) / 32; - y = (my - SY) / 32 - MENU_SCREEN_START_YPOS; + // fade music if info screen music differs from current music + if (!strEqual(curr_music, next_music)) + FadeMusic(); } - else if (dx || dy) /* keyboard input */ - { - if (dx && choice == 0) - x = (dx < 0 ? 10 : 12); - else if ((dx && choice == 1) || - (dx == +1 && choice == 2) || - (dx == -1 && choice == pos_end)) - button = MB_MENU_CHOICE; - else if (dy) - y = choice + dy; +} - if (y >= pos_empty1 && y <= pos_empty2) - y = (dy > 0 ? pos_empty2 + 1 : pos_empty1 - 1); - } +static void FadeInfoSoundsAndMusic(void) +{ + FadeInfoSounds(); + FadeInfoMusic(); +} - if (y == 0 && ((x == 0 && !button) || ((x == 10 || x == 12) && button))) - { - static unsigned long delay = 0; +static void DrawCursorAndText_Menu_Ext(struct TokenInfo *token_info, + int screen_pos, int menu_info_pos_raw, + boolean active) +{ + int pos = (menu_info_pos_raw < 0 ? screen_pos : menu_info_pos_raw); + struct TokenInfo *ti = &token_info[pos]; + int xpos = MENU_SCREEN_START_XPOS; + int ypos = MENU_SCREEN_START_YPOS + screen_pos; + int font_nr = getMenuTextFont(ti->type); - if (!DelayReached(&delay, GADGET_FRAME_DELAY)) - goto out; + if (setup_mode == SETUP_MODE_INPUT) + font_nr = FONT_MENU_1; - player_nr = (player_nr + (x == 10 ? -1 : +1) + MAX_PLAYERS) % MAX_PLAYERS; + if (active) + font_nr = FONT_ACTIVE(font_nr); - drawPlayerSetupInputInfo(player_nr); - } - else if (x == 0 && y >= pos_start && y <= pos_end && - !(y >= pos_empty1 && y <= pos_empty2)) - { - if (button) - { - if (y != choice) - { - drawCursor(y, FC_RED); - drawCursor(choice, FC_BLUE); - choice = y; - } - } - else - { - if (y == 1) - { - char *device_name = setup.input[player_nr].joy.device_name; + DrawText(mSX + xpos * 32, mSY + ypos * 32, ti->text, font_nr); - if (!setup.input[player_nr].use_joystick) - { - int new_device_nr = (dx >= 0 ? 0 : MAX_PLAYERS - 1); + if (ti->type & ~TYPE_SKIP_ENTRY) + drawCursor(screen_pos, active); +} - setJoystickDeviceToNr(device_name, new_device_nr); - setup.input[player_nr].use_joystick = TRUE; - } - else - { - int device_nr = getJoystickNrFromDeviceName(device_name); - int new_device_nr = device_nr + (dx >= 0 ? +1 : -1); +static void DrawCursorAndText_Menu(int screen_pos, int menu_info_pos_raw, + boolean active) +{ + DrawCursorAndText_Menu_Ext(menu_info, screen_pos, menu_info_pos_raw, active); +} - if (new_device_nr < 0 || new_device_nr >= MAX_PLAYERS) - setup.input[player_nr].use_joystick = FALSE; - else - setJoystickDeviceToNr(device_name, new_device_nr); - } +static void DrawCursorAndText_Setup(int screen_pos, int menu_info_pos_raw, + boolean active) +{ + DrawCursorAndText_Menu_Ext(setup_info, screen_pos, menu_info_pos_raw, active); +} - drawPlayerSetupInputInfo(player_nr); - } - else if (y == 2) - { - if (setup.input[player_nr].use_joystick) - { - InitJoysticks(); - CalibrateJoystick(player_nr); - } - else - CustomizeKeyboard(player_nr); - } - else if (y == pos_end) +static char *window_size_text; +static char *scaling_type_text; +static char *network_server_text; + +static void drawSetupValue(int, int); + +static void drawMenuInfoList(int first_entry, int num_page_entries, + int max_page_entries) +{ + int i; + + if (first_entry + num_page_entries > max_page_entries) + first_entry = 0; + + clearMenuListArea(); + + for (i = 0; i < num_page_entries; i++) + { + int menu_info_pos = first_entry + i; + struct TokenInfo *si = &menu_info[menu_info_pos]; + void *value_ptr = si->value; + + // set some entries to "unchangeable" according to other variables + if ((value_ptr == &setup.sound_simple && !audio.sound_available) || + (value_ptr == &setup.sound_loops && !audio.loops_available) || + (value_ptr == &setup.sound_music && !audio.music_available) || + (value_ptr == &setup.fullscreen && !video.fullscreen_available) || + (value_ptr == &window_size_text && !video.window_scaling_available) || + (value_ptr == &scaling_type_text && !video.window_scaling_available)) + si->type |= TYPE_GHOSTED; + + if (si->type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST)) + initCursor(i, IMG_MENU_BUTTON_ENTER_MENU); + else if (si->type & (TYPE_LEAVE_MENU|TYPE_LEAVE_LIST)) + initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU); + else if (si->type & ~TYPE_SKIP_ENTRY) + initCursor(i, IMG_MENU_BUTTON); + + DrawCursorAndText_Menu(i, menu_info_pos, FALSE); + + if (si->type & TYPE_STRING) + { + int gadget_id = -1; + + if (value_ptr == &network_server_text) + gadget_id = SCREEN_CTRL_ID_NETWORK_SERVER; + + if (gadget_id != -1) { - InitJoysticks(); + struct GadgetInfo *gi = screen_gadget[gadget_id]; + int xpos = MENU_SCREEN_START_XPOS; + int ypos = MENU_SCREEN_START_YPOS + i; + int x = mSX + xpos * 32; + int y = mSY + ypos * 32; - setup_mode = SETUP_MODE_MAIN; - DrawSetupScreen(); + ModifyGadget(gi, GDI_X, x, GDI_Y, y, GDI_END); } } - } - - BackToFront(); - - out: - if (game_status == SETUP) - DoAnimation(); + if (si->type & TYPE_VALUE && + menu_info == setup_info) + drawSetupValue(i, menu_info_pos); + } } -void CustomizeKeyboard(int player_nr) +static void DrawInfoScreen_Main(void) { + int fade_mask = REDRAW_FIELD; int i; - int step_nr; - boolean finished = FALSE; - static struct SetupKeyboardInfo custom_key; - static struct - { - Key *key; - char *text; - } customize_step[] = + + // (needed after displaying info sub-screens directly from main menu) + if (info_screens_from_main) { - { &custom_key.left, "Move Left" }, - { &custom_key.right, "Move Right" }, - { &custom_key.up, "Move Up" }, - { &custom_key.down, "Move Down" }, - { &custom_key.snap, "Snap Field" }, - { &custom_key.bomb, "Place Bomb" } - }; + info_screens_from_main = FALSE; - /* read existing key bindings from player setup */ - custom_key = setup.input[player_nr].key; + SetGameStatus(GAME_MODE_MAIN); - ClearWindow(); - DrawText(SX + 16, SY + 16, "Keyboard Input", FS_BIG, FC_YELLOW); + DrawMainMenu(); - BackToFront(); - InitAnimation(); + return; + } - step_nr = 0; - DrawText(SX, SY + (2+2*step_nr)*32, - customize_step[step_nr].text, FS_BIG, FC_RED); - DrawText(SX, SY + (2+2*step_nr+1)*32, - "Key:", FS_BIG, FC_RED); - DrawText(SX + 4*32, SY + (2+2*step_nr+1)*32, - getKeyNameFromKey(*customize_step[step_nr].key), - FS_BIG, FC_BLUE); + if (redraw_mask & REDRAW_ALL) + fade_mask = REDRAW_ALL; - while(!finished) - { - if (PendingEvent()) /* got event */ - { - Event event; + if (CheckFadeAll()) + fade_mask = REDRAW_ALL; - NextEvent(&event); + UnmapAllGadgets(); + FadeMenuSoundsAndMusic(); - switch(event.type) - { - case EVENT_KEYPRESS: - { - Key key = GetEventKey((KeyEvent *)&event, FALSE); + FreeScreenGadgets(); + CreateScreenGadgets(); - if (key == KSYM_Escape || (key == KSYM_Return && step_nr == 6)) - { - finished = TRUE; - break; - } + // (needed after displaying title screens which disable auto repeat) + KeyboardAutoRepeatOn(); - /* all keys configured -- wait for "Escape" or "Return" key */ - if (step_nr == 6) - break; + FadeSetLeaveScreen(); - /* press 'Enter' to keep the existing key binding */ - if (key == KSYM_Return) - key = *customize_step[step_nr].key; + FadeOut(fade_mask); - /* check if key already used */ - for (i=0; i