// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 2011-2016 by Matthew "Inuyasha" Walsh. // Copyright (C) 1999-2018 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file m_menu.c /// \brief XMOD's extremely revamped menu system. #ifdef __GNUC__ #include #endif #include "m_menu.h" #include "doomdef.h" #include "d_main.h" #include "d_netcmd.h" #include "console.h" #include "r_local.h" #include "hu_stuff.h" #include "g_game.h" #include "g_input.h" #include "m_argv.h" // Data. #include "sounds.h" #include "s_sound.h" #include "i_system.h" // Addfile #include "filesrch.h" #include "v_video.h" #include "i_video.h" #include "keys.h" #include "z_zone.h" #include "w_wad.h" #include "p_local.h" #include "p_setup.h" #include "f_finale.h" #ifdef HWRENDER #include "hardware/hw_main.h" #endif #include "d_net.h" #include "mserv.h" #include "m_misc.h" #include "m_anigif.h" #include "byteptr.h" #include "st_stuff.h" #include "i_sound.h" #include "k_kart.h" // SRB2kart #include "d_player.h" // KITEM_ constants #include "i_joy.h" // for joystick menu controls // Condition Sets #include "m_cond.h" // And just some randomness for the exits. #include "m_random.h" #if defined(HAVE_SDL) #include "SDL.h" #if SDL_VERSION_ATLEAST(2,0,0) #include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG #endif #endif #ifdef PC_DOS #include // for snprintf int snprintf(char *str, size_t n, const char *fmt, ...); //int vsnprintf(char *str, size_t n, const char *fmt, va_list ap); #endif #define SKULLXOFF -32 #define LINEHEIGHT 16 #define STRINGHEIGHT 8 #define FONTBHEIGHT 20 #define SMALLLINEHEIGHT 8 #define SLIDER_RANGE 10 #define SLIDER_WIDTH (8*SLIDER_RANGE+6) #define SERVERS_PER_PAGE 11 typedef enum { QUITMSG = 0, QUITMSG1, QUITMSG2, QUITMSG3, QUITMSG4, QUITMSG5, QUITMSG6, QUITMSG7, QUIT2MSG, QUIT2MSG1, QUIT2MSG2, QUIT2MSG3, QUIT2MSG4, QUIT2MSG5, QUIT2MSG6, QUIT3MSG, QUIT3MSG1, QUIT3MSG2, QUIT3MSG3, QUIT3MSG4, QUIT3MSG5, QUIT3MSG6, NUM_QUITMESSAGES } text_enum; const char *quitmsg[NUM_QUITMESSAGES]; // Stuff for customizing the player select screen Tails 09-22-2003 description_t description[32] = { {"\x82Sonic\x80\n\x82Speed:\x80 7\n\x82Weight:\x80 3", "", "sonic"}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""}, {"???", "", ""} }; //static char *char_notes = NULL; //static fixed_t char_scroll = 0; boolean menuactive = false; boolean fromlevelselect = false; typedef enum { LLM_CREATESERVER, LLM_LEVELSELECT, LLM_RECORDATTACK, LLM_NIGHTSATTACK } levellist_mode_t; levellist_mode_t levellistmode = LLM_CREATESERVER; UINT8 maplistoption = 0; static char joystickInfo[8][29]; #ifndef NONET static UINT32 serverlistpage; #endif //static saveinfo_t savegameinfo[MAXSAVEGAMES]; // Extra info about the save games. INT16 startmap; // Mario, NiGHTS, or just a plain old normal game? static INT16 itemOn = 1; // menu item skull is on, Hack by Tails 09-18-2002 static INT16 skullAnimCounter = 10; // skull animation counter static UINT8 setupcontrolplayer; static INT32 (*setupcontrols)[2]; // pointer to the gamecontrols of the player being edited // shhh... what am I doing... nooooo! static INT32 vidm_testingmode = 0; static INT32 vidm_previousmode; static INT32 vidm_selected = 0; static INT32 vidm_nummodes; static INT32 vidm_column_size; // // PROTOTYPES // static void M_StopMessage(INT32 choice); #ifndef NONET static void M_HandleServerPage(INT32 choice); static void M_RoomMenu(INT32 choice); #endif // Prototyping is fun, innit? // ========================================================================== // NEEDED FUNCTION PROTOTYPES GO HERE // ========================================================================== // the haxor message menu menu_t MessageDef; menu_t SPauseDef; #define lsheadingheight 16 // Sky Room //static void M_CustomLevelSelect(INT32 choice); //static void M_CustomWarp(INT32 choice); FUNCNORETURN static ATTRNORETURN void M_UltimateCheat(INT32 choice); //static void M_LoadGameLevelSelect(INT32 choice); static void M_GetAllEmeralds(INT32 choice); static void M_DestroyRobots(INT32 choice); //static void M_LevelSelectWarp(INT32 choice); static void M_Credits(INT32 choice); static void M_PandorasBox(INT32 choice); static void M_EmblemHints(INT32 choice); static char *M_GetConditionString(condition_t cond); menu_t SR_MainDef, SR_UnlockChecklistDef; // Misc. Main Menu #if 0 // Bring this back when we have actual single-player static void M_SinglePlayerMenu(INT32 choice); #endif static void M_Options(INT32 choice); static void M_Manual(INT32 choice); static void M_SelectableClearMenus(INT32 choice); static void M_Retry(INT32 choice); static void M_EndGame(INT32 choice); static void M_MapChange(INT32 choice); static void M_ChangeLevel(INT32 choice); static void M_ConfirmSpectate(INT32 choice); static void M_ConfirmEnterGame(INT32 choice); static void M_ConfirmTeamScramble(INT32 choice); static void M_ConfirmTeamChange(INT32 choice); static void M_ConfirmSpectateChange(INT32 choice); //static void M_SecretsMenu(INT32 choice); //static void M_SetupChoosePlayer(INT32 choice); static void M_QuitSRB2(INT32 choice); menu_t SP_MainDef, MP_MainDef, OP_MainDef; menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef, MISC_ChangeSpectateDef; // Single Player //static void M_LoadGame(INT32 choice); static void M_TimeAttack(INT32 choice); static boolean M_QuitTimeAttackMenu(void); //static void M_NightsAttack(INT32 choice); static void M_Statistics(INT32 choice); static void M_HandleStaffReplay(INT32 choice); static void M_ReplayTimeAttack(INT32 choice); static void M_ChooseTimeAttack(INT32 choice); //static void M_ChooseNightsAttack(INT32 choice); static void M_ModeAttackRetry(INT32 choice); static void M_ModeAttackEndGame(INT32 choice); static void M_SetGuestReplay(INT32 choice); //static void M_ChoosePlayer(INT32 choice); menu_t SP_LevelStatsDef; static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef; //static menu_t SP_NightsAttackDef, SP_NightsReplayDef, SP_NightsGuestReplayDef, SP_NightsGhostDef; // Multiplayer #ifndef NONET static void M_StartServerMenu(INT32 choice); static void M_ConnectMenu(INT32 choice); static void M_ConnectMenuModChecks(INT32 choice); static void M_Refresh(INT32 choice); static void M_Connect(INT32 choice); static void M_ChooseRoom(INT32 choice); #endif static void M_StartOfflineServerMenu(INT32 choice); static void M_StartServer(INT32 choice); static void M_SetupMultiPlayer(INT32 choice); static void M_SetupMultiPlayer2(INT32 choice); static void M_SetupMultiPlayer3(INT32 choice); static void M_SetupMultiPlayer4(INT32 choice); static void M_SetupMultiHandler(INT32 choice); // Options // Split into multiple parts due to size // Controls menu_t OP_ControlsDef, OP_AllControlsDef; menu_t OP_MouseOptionsDef, OP_Mouse2OptionsDef; menu_t OP_Joystick1Def, OP_Joystick2Def, OP_Joystick3Def, OP_Joystick4Def; static void M_VideoModeMenu(INT32 choice); static void M_Setup1PControlsMenu(INT32 choice); static void M_Setup2PControlsMenu(INT32 choice); static void M_Setup3PControlsMenu(INT32 choice); static void M_Setup4PControlsMenu(INT32 choice); static void M_Setup1PJoystickMenu(INT32 choice); static void M_Setup2PJoystickMenu(INT32 choice); static void M_Setup3PJoystickMenu(INT32 choice); static void M_Setup4PJoystickMenu(INT32 choice); static void M_AssignJoystick(INT32 choice); static void M_ChangeControl(INT32 choice); static void M_ResetControls(INT32 choice); // Video & Sound menu_t OP_VideoOptionsDef, OP_VideoModeDef; #ifdef HWRENDER menu_t OP_OpenGLOptionsDef, OP_OpenGLFogDef, OP_OpenGLColorDef; #endif menu_t OP_SoundOptionsDef; //static void M_RestartAudio(void); //Misc menu_t /*OP_DataOptionsDef,*/ OP_ScreenshotOptionsDef, OP_EraseDataDef; menu_t OP_HUDOptionsDef, OP_ChatOptionsDef; menu_t OP_GameOptionsDef, OP_ServerOptionsDef; #ifndef NONET menu_t OP_AdvServerOptionsDef; #endif //menu_t OP_NetgameOptionsDef, OP_GametypeOptionsDef; menu_t OP_MonitorToggleDef; static void M_ScreenshotOptions(INT32 choice); static void M_EraseData(INT32 choice); static void M_Addons(INT32 choice); static void M_AddonsOptions(INT32 choice); static patch_t *addonsp[NUM_EXT+5]; #define numaddonsshown 4 // Replay hut static void M_ReplayHut(INT32 choice); static void M_HandleReplayHutList(INT32 choice); static void M_DrawReplayHut(void); static boolean M_QuitReplayHut(void); static void M_EnterReplayOptions(INT32 choice); // Drawing functions static void M_DrawGenericMenu(void); static void M_DrawGenericBackgroundMenu(void); static void M_DrawCenteredMenu(void); static void M_DrawAddons(void); static void M_DrawSkyRoom(void); static void M_DrawChecklist(void); static void M_DrawEmblemHints(void); static void M_DrawPauseMenu(void); static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade); static void M_DrawServerMenu(void); static void M_DrawImageDef(void); //static void M_DrawLoad(void); static void M_DrawLevelStats(void); static void M_DrawTimeAttackMenu(void); //static void M_DrawNightsAttackMenu(void); //static void M_DrawSetupChoosePlayerMenu(void); static void M_DrawControl(void); static void M_DrawVideoMenu(void); static void M_DrawHUDOptions(void); static void M_DrawVideoMode(void); static void M_DrawMonitorToggles(void); #ifdef HWRENDER static void M_OGL_DrawFogMenu(void); static void M_OGL_DrawColorMenu(void); #endif static void M_DrawMPMainMenu(void); #ifndef NONET static void M_DrawConnectMenu(void); static void M_DrawRoomMenu(void); #endif static void M_DrawJoystick(void); static void M_DrawSetupMultiPlayerMenu(void); // Handling functions #ifndef NONET static boolean M_CancelConnect(void); #endif static boolean M_ExitPandorasBox(void); static boolean M_QuitMultiPlayerMenu(void); static void M_HandleAddons(INT32 choice); static void M_HandleSoundTest(INT32 choice); static void M_HandleImageDef(INT32 choice); //static void M_HandleLoadSave(INT32 choice); static void M_HandleLevelStats(INT32 choice); #ifndef NONET static void M_HandleConnectIP(INT32 choice); #endif static void M_HandleSetupMultiPlayer(INT32 choice); #ifdef HWRENDER static void M_HandleFogColor(INT32 choice); #endif static void M_HandleVideoMode(INT32 choice); static void M_HandleMonitorToggles(INT32 choice); // Consvar onchange functions static void Nextmap_OnChange(void); static void Newgametype_OnChange(void); static void Dummymenuplayer_OnChange(void); //static void Dummymares_OnChange(void); static void Dummystaff_OnChange(void); // ========================================================================== // CONSOLE VARIABLES AND THEIR POSSIBLE VALUES GO HERE. // ========================================================================== consvar_t cv_showfocuslost = {"showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL }; static CV_PossibleValue_t map_cons_t[] = { {0,"MIN"}, {NUMMAPS, "MAX"}, {0, NULL} }; consvar_t cv_nextmap = {"nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; consvar_t cv_chooseskin = {"chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange, 0, NULL, NULL, 0, 0, NULL}; // This gametype list is integral for many different reasons. // When you add gametypes here, don't forget to update them in CV_AddValue! CV_PossibleValue_t gametype_cons_t[] = { {GT_RACE, "Race"}, {GT_MATCH, "Battle"}, /* // SRB2kart {GT_COOP, "Co-op"}, {GT_COMPETITION, "Competition"}, {GT_RACE, "Race"}, {GT_MATCH, "Match"}, {GT_TEAMMATCH, "Team Match"}, {GT_TAG, "Tag"}, {GT_HIDEANDSEEK, "Hide and Seek"}, {GT_CTF, "CTF"}, */ {0, NULL} }; consvar_t cv_newgametype = {"newgametype", "Race", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t serversort_cons_t[] = { {0,"Ping"}, {1,"Modified State"}, {2,"Most Players"}, {3,"Least Players"}, {4,"Max Player Slots"}, {5,"Gametype"}, {0,NULL} }; consvar_t cv_serversort = {"serversort", "Ping", CV_CALL, serversort_cons_t, M_SortServerList, 0, NULL, NULL, 0, 0, NULL}; // autorecord demos for time attack static consvar_t cv_autorecord = {"autorecord", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; CV_PossibleValue_t ghost_cons_t[] = {{0, "Hide"}, {1, "Show Character"}, {2, "Show All"}, {0, NULL}}; CV_PossibleValue_t ghost2_cons_t[] = {{0, "Hide"}, {1, "Show"}, {0, NULL}}; consvar_t cv_ghost_besttime = {"ghost_besttime", "Show All", CV_SAVE, ghost_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_ghost_bestlap = {"ghost_bestlap", "Show All", CV_SAVE, ghost_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_ghost_last = {"ghost_last", "Show All", CV_SAVE, ghost_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_ghost_guest = {"ghost_guest", "Show", CV_SAVE, ghost2_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_ghost_staff = {"ghost_staff", "Show", CV_SAVE, ghost2_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //Console variables used solely in the menu system. //todo: add a way to use non-console variables in the menu // or make these consvars legitimate like color or skin. static void Splitplayers_OnChange(void); CV_PossibleValue_t splitplayers_cons_t[] = {{1, "One"}, {2, "Two"}, {3, "Three"}, {4, "Four"}, {0, NULL}}; consvar_t cv_splitplayers = {"splitplayers", "One", CV_CALL, splitplayers_cons_t, Splitplayers_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t dummymenuplayer_cons_t[] = {{0, "NOPE"}, {1, "P1"}, {2, "P2"}, {3, "P3"}, {4, "P4"}, {0, NULL}}; static CV_PossibleValue_t dummyteam_cons_t[] = {{0, "Spectator"}, {1, "Red"}, {2, "Blue"}, {0, NULL}}; static CV_PossibleValue_t dummyspectate_cons_t[] = {{0, "Spectator"}, {1, "Playing"}, {0, NULL}}; static CV_PossibleValue_t dummyscramble_cons_t[] = {{0, "Random"}, {1, "Points"}, {0, NULL}}; static CV_PossibleValue_t ringlimit_cons_t[] = {{0, "MIN"}, {9999, "MAX"}, {0, NULL}}; static CV_PossibleValue_t liveslimit_cons_t[] = {{0, "MIN"}, {99, "MAX"}, {0, NULL}}; /*static CV_PossibleValue_t dummymares_cons_t[] = { {-1, "END"}, {0,"Overall"}, {1,"Mare 1"}, {2,"Mare 2"}, {3,"Mare 3"}, {4,"Mare 4"}, {5,"Mare 5"}, {6,"Mare 6"}, {7,"Mare 7"}, {8,"Mare 8"}, {0,NULL} };*/ static CV_PossibleValue_t dummystaff_cons_t[] = {{0, "MIN"}, {100, "MAX"}, {0, NULL}}; static consvar_t cv_dummymenuplayer = {"dummymenuplayer", "P1", CV_HIDEN|CV_CALL, dummymenuplayer_cons_t, Dummymenuplayer_OnChange, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_dummyteam = {"dummyteam", "Spectator", CV_HIDEN, dummyteam_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_dummyspectate = {"dummyspectate", "Spectator", CV_HIDEN, dummyspectate_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_dummyscramble = {"dummyscramble", "Random", CV_HIDEN, dummyscramble_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_dummyrings = {"dummyrings", "0", CV_HIDEN, ringlimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_dummylives = {"dummylives", "0", CV_HIDEN, liveslimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_dummycontinues = {"dummycontinues", "0", CV_HIDEN, liveslimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //static consvar_t cv_dummymares = {"dummymares", "Overall", CV_HIDEN|CV_CALL, dummymares_cons_t, Dummymares_OnChange, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange, 0, NULL, NULL, 0, 0, NULL}; // ========================================================================== // ORGANIZATION START. // ========================================================================== // Note: Never should we be jumping from one category of menu options to another // without first going to the Main Menu. // Note: Ignore the above if you're working with the Pause menu. // Note: (Prefix)_MainMenu should be the target of all Main Menu options that // point to submenus. // --------- // Main Menu // --------- static menuitem_t MainMenu[] = { {IT_SUBMENU|IT_STRING, NULL, "Extras", &SR_MainDef, 76}, //{IT_CALL |IT_STRING, NULL, "1 Player", M_SinglePlayerMenu, 84}, {IT_CALL |IT_STRING, NULL, "Time Attack", M_TimeAttack, 84}, {IT_SUBMENU|IT_STRING, NULL, "Multiplayer", &MP_MainDef, 92}, {IT_CALL |IT_STRING, NULL, "Options", M_Options, 100}, {IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 108}, {IT_CALL |IT_STRING, NULL, "Quit Game", M_QuitSRB2, 116}, }; typedef enum { secrets = 0, singleplr, multiplr, options, addons, quitdoom } main_e; static menuitem_t MISC_AddonsMenu[] = { {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleAddons, 0}, // dummy menuitem for the control func }; static menuitem_t MISC_ReplayHutMenu[] = { {IT_CALL |IT_STRING, NULL, "Replay Options...", M_EnterReplayOptions, 0}, {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleReplayHutList, 20}, // Dummy menuitem for the replay list {IT_NOTHING, NULL, "", NULL, 20}, // Dummy for handling wrapping to the top of the menu.. }; static menuitem_t MISC_ReplayOptionsMenu[] = { {IT_CVAR|IT_STRING, NULL, "Record Replays", &cv_recordmultiplayerdemos, 0}, {IT_CVAR|IT_STRING, NULL, "Sync Check Interval", &cv_netdemosyncquality, 10}, }; // --------------------------------- // Pause Menu Mode Attacking Edition // --------------------------------- static menuitem_t MAPauseMenu[] = { {IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus,48}, {IT_CALL | IT_STRING, NULL, "Retry", M_ModeAttackRetry, 56}, {IT_CALL | IT_STRING, NULL, "Abort", M_ModeAttackEndGame, 64}, }; typedef enum { mapause_continue, mapause_retry, mapause_abort } mapause_e; // --------------------- // Pause Menu MP Edition // --------------------- static menuitem_t MPauseMenu[] = { {IT_STRING | IT_CALL, NULL, "Add-ons...", M_Addons, 8}, {IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16}, {IT_STRING | IT_CALL, NULL, "Switch Map..." , M_MapChange, 24}, {IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus, 40}, {IT_CALL | IT_STRING, NULL, "P1 Setup...", M_SetupMultiPlayer, 48}, // splitscreen {IT_CALL | IT_STRING, NULL, "P2 Setup...", M_SetupMultiPlayer2, 56}, // splitscreen {IT_CALL | IT_STRING, NULL, "P3 Setup...", M_SetupMultiPlayer3, 64}, // splitscreen {IT_CALL | IT_STRING, NULL, "P4 Setup...", M_SetupMultiPlayer4, 72}, // splitscreen {IT_STRING | IT_CALL, NULL, "Spectate", M_ConfirmSpectate, 48}, // alone {IT_STRING | IT_CALL, NULL, "Enter Game", M_ConfirmEnterGame, 48}, // alone {IT_STRING | IT_CALL, NULL, "Cancel Join", M_ConfirmSpectate, 48}, // alone {IT_STRING | IT_SUBMENU, NULL, "Switch Team...", &MISC_ChangeTeamDef, 48}, {IT_STRING | IT_SUBMENU, NULL, "Enter/Spectate...", &MISC_ChangeSpectateDef,48}, {IT_CALL | IT_STRING, NULL, "Player Setup...", M_SetupMultiPlayer, 56}, // alone {IT_CALL | IT_STRING, NULL, "Options", M_Options, 64}, {IT_CALL | IT_STRING, NULL, "Return to Title", M_EndGame, 80}, {IT_CALL | IT_STRING, NULL, "Quit Game", M_QuitSRB2, 88}, }; typedef enum { mpause_addons = 0, mpause_scramble, mpause_switchmap, mpause_continue, mpause_psetupsplit, mpause_psetupsplit2, mpause_psetupsplit3, mpause_psetupsplit4, mpause_spectate, mpause_entergame, mpause_canceljoin, mpause_switchteam, mpause_switchspectate, mpause_psetup, mpause_options, mpause_title, mpause_quit } mpause_e; // --------------------- // Pause Menu SP Edition // --------------------- static menuitem_t SPauseMenu[] = { // Pandora's Box will be shifted up if both options are available {IT_CALL | IT_STRING, NULL, "Pandora's Box...", M_PandorasBox, 16}, {IT_CALL | IT_STRING, NULL, "Medal Hints...", M_EmblemHints, 24}, //{IT_CALL | IT_STRING, NULL, "Level Select...", M_LoadGameLevelSelect, 32}, {IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus,48}, {IT_CALL | IT_STRING, NULL, "Retry", M_Retry, 56}, {IT_CALL | IT_STRING, NULL, "Options", M_Options, 64}, {IT_CALL | IT_STRING, NULL, "Return to Title", M_EndGame, 80}, {IT_CALL | IT_STRING, NULL, "Quit Game", M_QuitSRB2, 88}, }; typedef enum { spause_pandora = 0, spause_hints, //spause_levelselect, spause_continue, spause_retry, spause_options, spause_title, spause_quit } spause_e; // ----------------- // Misc menu options // ----------------- // Prefix: MISC_ static menuitem_t MISC_ScrambleTeamMenu[] = { {IT_STRING|IT_CVAR, NULL, "Scramble Method", &cv_dummyscramble, 30}, {IT_WHITESTRING|IT_CALL, NULL, "Confirm", M_ConfirmTeamScramble, 90}, }; static menuitem_t MISC_ChangeTeamMenu[] = { {IT_STRING|IT_CVAR, NULL, "Player", &cv_dummymenuplayer, 30}, {IT_STRING|IT_CVAR, NULL, "Team", &cv_dummyteam, 40}, {IT_WHITESTRING|IT_CALL, NULL, "Confirm", M_ConfirmTeamChange, 90}, }; static menuitem_t MISC_ChangeSpectateMenu[] = { {IT_STRING|IT_CVAR, NULL, "Player", &cv_dummymenuplayer, 30}, {IT_STRING|IT_CVAR, NULL, "Status", &cv_dummyspectate, 40}, {IT_WHITESTRING|IT_CALL, NULL, "Confirm", M_ConfirmSpectateChange, 90}, }; static menuitem_t MISC_ChangeLevelMenu[] = { {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68}, {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78}, {IT_WHITESTRING|IT_CALL, NULL, "Change Level", M_ChangeLevel, 130}, }; static menuitem_t MISC_HelpMenu[] = { {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL00", M_HandleImageDef, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL01", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL02", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL03", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL04", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL05", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL06", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL07", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL08", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL09", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL10", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL11", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL12", M_HandleImageDef, 1}, {IT_KEYHANDLER | IT_NOTHING, NULL, "MANUAL99", M_HandleImageDef, 0}, }; // -------------------------------- // Sky Room and all of its submenus // -------------------------------- // Prefix: SR_ // Pause Menu Pandora's Box Options static menuitem_t SR_PandorasBox[] = { {IT_STRING | IT_CVAR, NULL, "Rings", &cv_dummyrings, 20}, {IT_STRING | IT_CVAR, NULL, "Lives", &cv_dummylives, 30}, {IT_STRING | IT_CVAR, NULL, "Continues", &cv_dummycontinues, 40}, {IT_STRING | IT_CVAR, NULL, "Gravity", &cv_gravity, 60}, {IT_STRING | IT_CVAR, NULL, "Throw Rings", &cv_ringslinger, 70}, {IT_STRING | IT_CALL, NULL, "Get All Emeralds", M_GetAllEmeralds, 90}, {IT_STRING | IT_CALL, NULL, "Destroy All Robots", M_DestroyRobots, 100}, {IT_STRING | IT_CALL, NULL, "Ultimate Cheat", M_UltimateCheat, 130}, }; // Sky Room Custom Unlocks static menuitem_t SR_MainMenu[] = { {IT_STRING|IT_SUBMENU, NULL, "Unlockables", &SR_UnlockChecklistDef, 0}, {IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 10}, {IT_CALL|IT_STRING, NULL, "Replay Hut", M_ReplayHut, 20}, {IT_DISABLED, NULL, "", NULL, 0}, // Custom1 {IT_DISABLED, NULL, "", NULL, 0}, // Custom2 {IT_DISABLED, NULL, "", NULL, 0}, // Custom3 {IT_DISABLED, NULL, "", NULL, 0}, // Custom4 {IT_DISABLED, NULL, "", NULL, 0}, // Custom5 {IT_DISABLED, NULL, "", NULL, 0}, // Custom6 {IT_DISABLED, NULL, "", NULL, 0}, // Custom7 {IT_DISABLED, NULL, "", NULL, 0}, // Custom8 {IT_DISABLED, NULL, "", NULL, 0}, // Custom9 {IT_DISABLED, NULL, "", NULL, 0}, // Custom10 {IT_DISABLED, NULL, "", NULL, 0}, // Custom11 {IT_DISABLED, NULL, "", NULL, 0}, // Custom12 {IT_DISABLED, NULL, "", NULL, 0}, // Custom13 {IT_DISABLED, NULL, "", NULL, 0}, // Custom14 {IT_DISABLED, NULL, "", NULL, 0}, // Custom15 {IT_DISABLED, NULL, "", NULL, 0}, // Custom16 {IT_DISABLED, NULL, "", NULL, 0}, // Custom17 {IT_DISABLED, NULL, "", NULL, 0}, // Custom18 {IT_DISABLED, NULL, "", NULL, 0}, // Custom19 {IT_DISABLED, NULL, "", NULL, 0}, // Custom20 {IT_DISABLED, NULL, "", NULL, 0}, // Custom21 {IT_DISABLED, NULL, "", NULL, 0}, // Custom22 {IT_DISABLED, NULL, "", NULL, 0}, // Custom23 {IT_DISABLED, NULL, "", NULL, 0}, // Custom24 {IT_DISABLED, NULL, "", NULL, 0}, // Custom25 {IT_DISABLED, NULL, "", NULL, 0}, // Custom26 {IT_DISABLED, NULL, "", NULL, 0}, // Custom27 {IT_DISABLED, NULL, "", NULL, 0}, // Custom28 {IT_DISABLED, NULL, "", NULL, 0}, // Custom29 {IT_DISABLED, NULL, "", NULL, 0}, // Custom30 {IT_DISABLED, NULL, "", NULL, 0}, // Custom31 {IT_DISABLED, NULL, "", NULL, 0}, // Custom32 }; /*static menuitem_t SR_LevelSelectMenu[] = { {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78}, {IT_WHITESTRING|IT_CALL, NULL, "Start", M_LevelSelectWarp, 130}, };*/ static menuitem_t SR_UnlockChecklistMenu[] = { {IT_SUBMENU | IT_STRING, NULL, "NEXT", &MainDef, 192}, }; static menuitem_t SR_EmblemHintMenu[] = { {IT_STRING|IT_CVAR, NULL, "Medal Radar", &cv_itemfinder, 10}, {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SPauseDef, 20} }; // -------------------------------- // 1 Player and all of its submenus // -------------------------------- // Prefix: SP_ // Single Player Main static menuitem_t SP_MainMenu[] = { //{IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 92}, {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 100}, //{IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 108}, {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 108}, }; enum { //sploadgame, sprecordattack, //spnightsmode, spstatistics }; // Single Player Load Game /*static menuitem_t SP_LoadGameMenu[] = { {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLoadSave, '\0'}, // dummy menuitem for the control func }; // Single Player Level Select static menuitem_t SP_LevelSelectMenu[] = { {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78}, {IT_WHITESTRING|IT_CALL, NULL, "Start", M_LevelSelectWarp, 130}, };*/ // Single Player Time Attack static menuitem_t SP_TimeAttackMenu[] = { {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Name", &cv_playername, 0}, {IT_STRING|IT_CVAR, NULL, "Character", &cv_chooseskin, 13}, {IT_STRING|IT_CVAR, NULL, "Color", &cv_playercolor, 26}, {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78}, {IT_DISABLED, NULL, "Guest...", &SP_GuestReplayDef, 98}, {IT_DISABLED, NULL, "Replay...", &SP_ReplayDef, 108}, {IT_WHITESTRING|IT_SUBMENU, NULL, "Ghosts...", &SP_GhostDef, 118}, {IT_WHITESTRING|IT_CALL|IT_CALL_NOTMODIFIED, NULL, "Start", M_ChooseTimeAttack, 130}, }; enum { taname, taplayer, tacolor, talevel, taguest, tareplay, taghost, tastart }; static menuitem_t SP_ReplayMenu[] = { {IT_WHITESTRING|IT_CALL, NULL, "Replay Best Time", M_ReplayTimeAttack, 90}, {IT_WHITESTRING|IT_CALL, NULL, "Replay Best Lap", M_ReplayTimeAttack, 98}, {IT_WHITESTRING|IT_CALL, NULL, "Replay Last", M_ReplayTimeAttack, 106}, {IT_WHITESTRING|IT_CALL, NULL, "Replay Guest", M_ReplayTimeAttack, 114}, {IT_WHITESTRING|IT_KEYHANDLER, NULL, "Replay Staff",M_HandleStaffReplay,122}, {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SP_TimeAttackDef, 130} }; /*static menuitem_t SP_NightsReplayMenu[] = { {IT_WHITESTRING|IT_CALL, NULL, "Replay Best Score", M_ReplayTimeAttack, 0}, {IT_WHITESTRING|IT_CALL, NULL, "Replay Best Time", M_ReplayTimeAttack,16}, {IT_WHITESTRING|IT_CALL, NULL, "Replay Last", M_ReplayTimeAttack,21}, {IT_WHITESTRING|IT_CALL, NULL, "Replay Guest", M_ReplayTimeAttack,29}, {IT_WHITESTRING|IT_KEYHANDLER, NULL, "Replay Staff",M_HandleStaffReplay,37}, {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SP_NightsAttackDef, 50} };*/ static menuitem_t SP_GuestReplayMenu[] = { {IT_WHITESTRING|IT_CALL, NULL, "Save Best Time as Guest", M_SetGuestReplay, 94}, {IT_WHITESTRING|IT_CALL, NULL, "Save Best Lap as Guest", M_SetGuestReplay,102}, {IT_WHITESTRING|IT_CALL, NULL, "Save Last as Guest", M_SetGuestReplay,110}, {IT_WHITESTRING|IT_CALL, NULL, "Delete Guest Replay", M_SetGuestReplay,120}, {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SP_TimeAttackDef, 130} }; /*static menuitem_t SP_NightsGuestReplayMenu[] = { {IT_WHITESTRING|IT_CALL, NULL, "Save Best Score as Guest", M_SetGuestReplay, 8}, {IT_WHITESTRING|IT_CALL, NULL, "Save Best Time as Guest", M_SetGuestReplay,16}, {IT_WHITESTRING|IT_CALL, NULL, "Save Last as Guest", M_SetGuestReplay,24}, {IT_WHITESTRING|IT_CALL, NULL, "Delete Guest Replay", M_SetGuestReplay,37}, {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SP_NightsAttackDef, 50} };*/ static menuitem_t SP_GhostMenu[] = { {IT_STRING|IT_CVAR, NULL, "Best Time", &cv_ghost_besttime, 88}, {IT_STRING|IT_CVAR, NULL, "Best Lap", &cv_ghost_bestlap, 96}, {IT_STRING|IT_CVAR, NULL, "Last", &cv_ghost_last, 104}, {IT_DISABLED, NULL, "Guest", &cv_ghost_guest, 112}, {IT_DISABLED, NULL, "Staff Attack",&cv_ghost_staff, 120}, {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SP_TimeAttackDef, 130} }; /*static menuitem_t SP_NightsGhostMenu[] = { {IT_STRING|IT_CVAR, NULL, "Best Score", &cv_ghost_bestscore, 0}, {IT_STRING|IT_CVAR, NULL, "Best Time", &cv_ghost_besttime, 8}, {IT_STRING|IT_CVAR, NULL, "Last", &cv_ghost_last, 16}, {IT_STRING|IT_CVAR, NULL, "Guest", &cv_ghost_guest, 29}, {IT_STRING|IT_CVAR, NULL, "Staff Attack",&cv_ghost_staff, 37}, {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SP_NightsAttackDef, 50} };*/ // Single Player Nights Attack /*static menuitem_t SP_NightsAttackMenu[] = { {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 44}, {IT_STRING|IT_CVAR, NULL, "Show Records For", &cv_dummymares, 54}, {IT_DISABLED, NULL, "Guest Option...", &SP_NightsGuestReplayDef, 108}, {IT_DISABLED, NULL, "Replay...", &SP_NightsReplayDef, 118}, {IT_DISABLED, NULL, "Ghosts...", &SP_NightsGhostDef, 128}, {IT_WHITESTRING|IT_CALL|IT_CALL_NOTMODIFIED, NULL, "Start", M_ChooseNightsAttack, 138}, };*/ enum { nalevel, narecords, naguest, nareplay, naghost, nastart }; // Statistics static menuitem_t SP_LevelStatsMenu[] = { {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelStats, '\0'}, // dummy menuitem for the control func }; // A rare case. // External files modify this menu, so we can't call it static. // And I'm too lazy to go through and rename it everywhere. ARRGH! #define M_ChoosePlayer NULL menuitem_t PlayerMenu[MAXSKINS]; // ----------------------------------- // Multiplayer and all of its submenus // ----------------------------------- // Prefix: MP_ static menuitem_t MP_MainMenu[] = { {IT_HEADER, NULL, "Players", NULL, 0}, {IT_STRING|IT_CVAR, NULL, "Number of local players", &cv_splitplayers, 10}, {IT_STRING|IT_KEYHANDLER,NULL, "Player setup...", M_SetupMultiHandler,18}, {IT_HEADER, NULL, "Host a game", NULL, 100-24}, #ifndef NONET {IT_STRING|IT_CALL, NULL, "Internet/LAN...", M_StartServerMenu, 110-24}, #else {IT_GRAYEDOUT, NULL, "Internet/LAN...", NULL, 110-24}, #endif {IT_STRING|IT_CALL, NULL, "Offline...", M_StartOfflineServerMenu, 118-24}, {IT_HEADER, NULL, "Join a game", NULL, 132-24}, #ifndef NONET {IT_STRING|IT_CALL, NULL, "Internet server browser...",M_ConnectMenuModChecks, 142-24}, {IT_STRING|IT_KEYHANDLER, NULL, "Specify IPv4 address:", M_HandleConnectIP, 150-24}, #else {IT_GRAYEDOUT, NULL, "Internet server browser...",NULL, 142-24}, {IT_GRAYEDOUT, NULL, "Specify IPv4 address:", NULL, 150-24}, #endif //{IT_HEADER, NULL, "Player setup", NULL, 80}, //{IT_STRING|IT_CALL, NULL, "Name, character, color...", M_SetupMultiPlayer, 90}, }; #ifndef NONET static menuitem_t MP_ServerMenu[] = { {IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 0}, {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 10}, {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 20}, {IT_STRING|IT_CVAR|IT_CV_PASSWORD, NULL, "Password", &cv_dummyjoinpassword, 44}, {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68}, {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78}, {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 130}, }; #endif // Separated offline and normal servers. static menuitem_t MP_OfflineServerMenu[] = { {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68}, {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78}, {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 130}, }; static menuitem_t MP_PlayerSetupMenu[] = { {IT_KEYHANDLER | IT_STRING, NULL, "Name", M_HandleSetupMultiPlayer, 0}, {IT_KEYHANDLER | IT_STRING, NULL, "Character", M_HandleSetupMultiPlayer, 16}, // Tails 01-18-2001 {IT_KEYHANDLER | IT_STRING, NULL, "Color", M_HandleSetupMultiPlayer, 152}, }; #ifndef NONET static menuitem_t MP_ConnectMenu[] = { {IT_STRING | IT_CALL, NULL, "Room...", M_RoomMenu, 4}, {IT_STRING | IT_CVAR, NULL, "Sort By", &cv_serversort, 12}, {IT_STRING | IT_KEYHANDLER, NULL, "Page", M_HandleServerPage, 20}, {IT_STRING | IT_CALL, NULL, "Refresh", M_Refresh, 28}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 48-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 60-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 72-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 84-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 96-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 108-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 120-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 132-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 144-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 156-4}, {IT_STRING | IT_SPACE, NULL, "", M_Connect, 168-4}, }; enum { mp_connect_room, mp_connect_sort, mp_connect_page, mp_connect_refresh, FIRSTSERVERLINE }; static menuitem_t MP_RoomMenu[] = { {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9}, {IT_DISABLED, NULL, "", M_ChooseRoom, 18}, {IT_DISABLED, NULL, "", M_ChooseRoom, 27}, {IT_DISABLED, NULL, "", M_ChooseRoom, 36}, {IT_DISABLED, NULL, "", M_ChooseRoom, 45}, {IT_DISABLED, NULL, "", M_ChooseRoom, 54}, {IT_DISABLED, NULL, "", M_ChooseRoom, 63}, {IT_DISABLED, NULL, "", M_ChooseRoom, 72}, {IT_DISABLED, NULL, "", M_ChooseRoom, 81}, {IT_DISABLED, NULL, "", M_ChooseRoom, 90}, {IT_DISABLED, NULL, "", M_ChooseRoom, 99}, {IT_DISABLED, NULL, "", M_ChooseRoom, 108}, {IT_DISABLED, NULL, "", M_ChooseRoom, 117}, {IT_DISABLED, NULL, "", M_ChooseRoom, 126}, {IT_DISABLED, NULL, "", M_ChooseRoom, 135}, {IT_DISABLED, NULL, "", M_ChooseRoom, 144}, {IT_DISABLED, NULL, "", M_ChooseRoom, 153}, {IT_DISABLED, NULL, "", M_ChooseRoom, 162}, }; #endif // ------------------------------------ // Options and most (?) of its submenus // ------------------------------------ // Prefix: OP_ static menuitem_t OP_MainMenu[] = { {IT_SUBMENU|IT_STRING, NULL, "Control Setup...", &OP_ControlsDef, 10}, {IT_SUBMENU|IT_STRING, NULL, "Video Options...", &OP_VideoOptionsDef, 30}, {IT_SUBMENU|IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 40}, {IT_SUBMENU|IT_STRING, NULL, "HUD Options...", &OP_HUDOptionsDef, 60}, {IT_STRING|IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 70}, {IT_SUBMENU|IT_STRING, NULL, "Gameplay Options...", &OP_GameOptionsDef, 90}, {IT_SUBMENU|IT_STRING, NULL, "Server Options...", &OP_ServerOptionsDef, 100}, {IT_STRING|IT_CALL, NULL, "Add-on Options...", M_AddonsOptions, 110}, {IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", M_Manual, 130}, {IT_CALL|IT_STRING, NULL, "Play Credits", M_Credits, 140}, {IT_SUBMENU|IT_STRING, NULL, "Erase Data...", &OP_EraseDataDef, 150}, }; static menuitem_t OP_ControlsMenu[] = { {IT_CALL | IT_STRING, NULL, "Player 1 Controls...", M_Setup1PControlsMenu, 10}, {IT_CALL | IT_STRING, NULL, "Player 2 Controls...", M_Setup2PControlsMenu, 20}, {IT_CALL | IT_STRING, NULL, "Player 3 Controls...", &M_Setup3PControlsMenu, 30}, {IT_CALL | IT_STRING, NULL, "Player 4 Controls...", &M_Setup4PControlsMenu, 40}, {IT_STRING | IT_CVAR, NULL, "Controls per key", &cv_controlperkey, 60}, }; static menuitem_t OP_AllControlsMenu[] = { {IT_SUBMENU|IT_STRING, NULL, "Gamepad Options...", &OP_Joystick1Def, 0}, {IT_CALL|IT_STRING, NULL, "Reset to defaults", M_ResetControls, 8}, //{IT_SPACE, NULL, NULL, NULL, 0}, {IT_HEADER, NULL, "Gameplay Controls", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, {IT_CONTROL, NULL, "Accelerate", M_ChangeControl, gc_accelerate }, {IT_CONTROL, NULL, "Turn Left", M_ChangeControl, gc_turnleft }, {IT_CONTROL, NULL, "Turn Right", M_ChangeControl, gc_turnright }, {IT_CONTROL, NULL, "Drift", M_ChangeControl, gc_drift }, {IT_CONTROL, NULL, "Brake", M_ChangeControl, gc_brake }, {IT_CONTROL, NULL, "Use/Throw Item", M_ChangeControl, gc_fire }, {IT_CONTROL, NULL, "Aim Forward", M_ChangeControl, gc_aimforward }, {IT_CONTROL, NULL, "Aim Backward", M_ChangeControl, gc_aimbackward}, {IT_CONTROL, NULL, "Look Backward", M_ChangeControl, gc_lookback }, {IT_HEADER, NULL, "Miscelleanous Controls", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, {IT_CONTROL, NULL, "Chat", M_ChangeControl, gc_talkkey }, //{IT_CONTROL, NULL, "Team Chat", M_ChangeControl, gc_teamkey }, {IT_CONTROL, NULL, "Show Rankings", M_ChangeControl, gc_scores }, {IT_CONTROL, NULL, "Change Viewpoint", M_ChangeControl, gc_viewpoint }, {IT_CONTROL, NULL, "Reset Camera", M_ChangeControl, gc_camreset }, {IT_CONTROL, NULL, "Toggle First-Person", M_ChangeControl, gc_camtoggle }, {IT_CONTROL, NULL, "Pause", M_ChangeControl, gc_pause }, {IT_CONTROL, NULL, "Screenshot", M_ChangeControl, gc_screenshot }, {IT_CONTROL, NULL, "Toggle GIF Recording", M_ChangeControl, gc_recordgif }, {IT_CONTROL, NULL, "Open/Close Menu (ESC)", M_ChangeControl, gc_systemmenu }, {IT_CONTROL, NULL, "Developer Console", M_ChangeControl, gc_console }, {IT_HEADER, NULL, "Spectator Controls", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, {IT_CONTROL, NULL, "Become Spectator", M_ChangeControl, gc_spectate }, {IT_CONTROL, NULL, "Look Up", M_ChangeControl, gc_lookup }, {IT_CONTROL, NULL, "Look Down", M_ChangeControl, gc_lookdown }, {IT_CONTROL, NULL, "Center View", M_ChangeControl, gc_centerview }, {IT_HEADER, NULL, "Custom Lua Actions", NULL, 0}, {IT_SPACE, NULL, NULL, NULL, 0}, {IT_CONTROL, NULL, "Custom Action 1", M_ChangeControl, gc_custom1 }, {IT_CONTROL, NULL, "Custom Action 2", M_ChangeControl, gc_custom2 }, {IT_CONTROL, NULL, "Custom Action 3", M_ChangeControl, gc_custom3 }, }; static menuitem_t OP_Joystick1Menu[] = { {IT_STRING | IT_CALL, NULL, "Select Gamepad..." , M_Setup1PJoystickMenu, 10}, {IT_STRING | IT_CVAR, NULL, "Aim Forward/Back" , &cv_aimaxis , 30}, {IT_STRING | IT_CVAR, NULL, "Turn Left/Right" , &cv_turnaxis , 40}, {IT_STRING | IT_CVAR, NULL, "Accelerate" , &cv_moveaxis , 50}, {IT_STRING | IT_CVAR, NULL, "Brake" , &cv_brakeaxis , 60}, {IT_STRING | IT_CVAR, NULL, "Drift" , &cv_driftaxis , 70}, {IT_STRING | IT_CVAR, NULL, "Use Item" , &cv_fireaxis , 80}, {IT_STRING | IT_CVAR, NULL, "Look Up/Down" , &cv_lookaxis , 90}, }; static menuitem_t OP_Joystick2Menu[] = { {IT_STRING | IT_CALL, NULL, "Select Gamepad..." , M_Setup2PJoystickMenu, 10}, {IT_STRING | IT_CVAR, NULL, "Aim Forward/Back" , &cv_aimaxis2 , 30}, {IT_STRING | IT_CVAR, NULL, "Turn Left/Right" , &cv_turnaxis2 , 40}, {IT_STRING | IT_CVAR, NULL, "Accelerate" , &cv_moveaxis2 , 50}, {IT_STRING | IT_CVAR, NULL, "Brake" , &cv_brakeaxis2 , 60}, {IT_STRING | IT_CVAR, NULL, "Drift" , &cv_driftaxis2 , 70}, {IT_STRING | IT_CVAR, NULL, "Use Item" , &cv_fireaxis2 , 80}, {IT_STRING | IT_CVAR, NULL, "Look Up/Down" , &cv_lookaxis2 , 90}, }; static menuitem_t OP_Joystick3Menu[] = { {IT_STRING | IT_CALL, NULL, "Select Gamepad..." , M_Setup3PJoystickMenu, 10}, {IT_STRING | IT_CVAR, NULL, "Aim Forward/Back" , &cv_aimaxis3 , 30}, {IT_STRING | IT_CVAR, NULL, "Turn Left/Right" , &cv_turnaxis3 , 40}, {IT_STRING | IT_CVAR, NULL, "Accelerate" , &cv_moveaxis3 , 50}, {IT_STRING | IT_CVAR, NULL, "Brake" , &cv_brakeaxis3 , 60}, {IT_STRING | IT_CVAR, NULL, "Drift" , &cv_driftaxis3 , 70}, {IT_STRING | IT_CVAR, NULL, "Use Item" , &cv_fireaxis3 , 80}, {IT_STRING | IT_CVAR, NULL, "Look Up/Down" , &cv_lookaxis3 , 90}, }; static menuitem_t OP_Joystick4Menu[] = { {IT_STRING | IT_CALL, NULL, "Select Gamepad..." , M_Setup4PJoystickMenu, 10}, {IT_STRING | IT_CVAR, NULL, "Aim Forward/Back" , &cv_aimaxis4 , 30}, {IT_STRING | IT_CVAR, NULL, "Turn Left/Right" , &cv_turnaxis4 , 40}, {IT_STRING | IT_CVAR, NULL, "Accelerate" , &cv_moveaxis4 , 50}, {IT_STRING | IT_CVAR, NULL, "Brake" , &cv_brakeaxis4 , 60}, {IT_STRING | IT_CVAR, NULL, "Drift" , &cv_driftaxis4 , 70}, {IT_STRING | IT_CVAR, NULL, "Use Item" , &cv_fireaxis4 , 80}, {IT_STRING | IT_CVAR, NULL, "Look Up/Down" , &cv_lookaxis4 , 90}, }; static menuitem_t OP_JoystickSetMenu[] = { {IT_CALL | IT_NOTHING, "None", NULL, M_AssignJoystick, LINEHEIGHT+5}, {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, (LINEHEIGHT*2)+5}, {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, (LINEHEIGHT*3)+5}, {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, (LINEHEIGHT*4)+5}, {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, (LINEHEIGHT*5)+5}, {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, (LINEHEIGHT*6)+5}, {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, (LINEHEIGHT*7)+5}, {IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, (LINEHEIGHT*8)+5}, }; /*static menuitem_t OP_MouseOptionsMenu[] = { {IT_STRING | IT_CVAR, NULL, "Use Mouse", &cv_usemouse, 10}, {IT_STRING | IT_CVAR, NULL, "First-Person MouseLook", &cv_alwaysfreelook, 30}, {IT_STRING | IT_CVAR, NULL, "Third-Person MouseLook", &cv_chasefreelook, 40}, {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove, 50}, {IT_STRING | IT_CVAR, NULL, "Invert Mouse", &cv_invertmouse, 60}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Mouse X Speed", &cv_mousesens, 70}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Mouse Y Speed", &cv_mouseysens, 80}, }; static menuitem_t OP_Mouse2OptionsMenu[] = { {IT_STRING | IT_CVAR, NULL, "Use Mouse 2", &cv_usemouse2, 10}, {IT_STRING | IT_CVAR, NULL, "Second Mouse Serial Port", &cv_mouse2port, 20}, {IT_STRING | IT_CVAR, NULL, "First-Person MouseLook", &cv_alwaysfreelook2, 30}, {IT_STRING | IT_CVAR, NULL, "Third-Person MouseLook", &cv_chasefreelook2, 40}, {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove2, 50}, {IT_STRING | IT_CVAR, NULL, "Invert Mouse", &cv_invertmouse2, 60}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Mouse X Speed", &cv_mousesens2, 70}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Mouse Y Speed", &cv_mouseysens2, 80}, };*/ static menuitem_t OP_VideoOptionsMenu[] = { {IT_STRING | IT_CALL, NULL, "Set Resolution...", M_VideoModeMenu, 10}, #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) {IT_STRING|IT_CVAR, NULL, "Fullscreen", &cv_fullscreen, 20}, #endif {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Gamma", &cv_usegamma, 30}, {IT_STRING | IT_CVAR, NULL, "Draw Distance", &cv_drawdist, 45}, //{IT_STRING | IT_CVAR, NULL, "NiGHTS Draw Dist", &cv_drawdist_nights, 55}, {IT_STRING | IT_CVAR, NULL, "Weather Draw Distance",&cv_drawdist_precip, 55}, //{IT_STRING | IT_CVAR, NULL, "Weather Density", &cv_precipdensity, 65}, {IT_STRING | IT_CVAR, NULL, "Skyboxes", &cv_skybox, 65}, {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 80}, {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 90}, #ifdef HWRENDER {IT_STRING | IT_CVAR, NULL, "3D models", &cv_grmd2, 105}, {IT_SUBMENU|IT_STRING, NULL, "OpenGL Options...", &OP_OpenGLOptionsDef, 115}, #endif }; enum { op_video_res = 0, #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) op_video_fullscreen, #endif op_video_gamma, op_video_dd, op_video_wdd, //op_video_wd, op_video_skybox, op_video_fps, op_video_vsync, #ifdef HWRENDER op_video_md2, op_video_ogl, #endif }; static menuitem_t OP_VideoModeMenu[] = { {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleVideoMode, '\0'}, // dummy menuitem for the control func }; #ifdef HWRENDER static menuitem_t OP_OpenGLOptionsMenu[] = { {IT_SUBMENU|IT_STRING, NULL, "Fog...", &OP_OpenGLFogDef, 10}, {IT_SUBMENU|IT_STRING, NULL, "Gamma...", &OP_OpenGLColorDef, 20}, {IT_STRING|IT_CVAR, NULL, "Field of View", &cv_fov, 35}, {IT_STRING|IT_CVAR, NULL, "Quality", &cv_scr_depth, 45}, {IT_STRING|IT_CVAR, NULL, "Texture Filter", &cv_grfiltermode, 55}, {IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_granisotropicmode, 65}, /*#ifdef _WINDOWS {IT_STRING|IT_CVAR, NULL, "Fullscreen", &cv_fullscreen, 50}, #endif #ifdef ALAM_LIGHTING {IT_SUBMENU|IT_STRING, NULL, "Lighting...", &OP_OpenGLLightingDef, 70}, #endif*/ }; #ifdef ALAM_LIGHTING static menuitem_t OP_OpenGLLightingMenu[] = { {IT_STRING|IT_CVAR, NULL, "Coronas", &cv_grcoronas, 0}, {IT_STRING|IT_CVAR, NULL, "Coronas size", &cv_grcoronasize, 10}, {IT_STRING|IT_CVAR, NULL, "Dynamic lighting", &cv_grdynamiclighting, 20}, {IT_STRING|IT_CVAR, NULL, "Static lighting", &cv_grstaticlighting, 30}, }; #endif static menuitem_t OP_OpenGLFogMenu[] = { {IT_STRING|IT_CVAR, NULL, "Fog", &cv_grfog, 10}, {IT_STRING|IT_KEYHANDLER, NULL, "Fog Color", M_HandleFogColor, 20}, {IT_STRING|IT_CVAR, NULL, "Fog Density", &cv_grfogdensity, 30}, {IT_STRING|IT_CVAR, NULL, "Software Fog",&cv_grsoftwarefog,40}, }; static menuitem_t OP_OpenGLColorMenu[] = { {IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "Red", &cv_grgammared, 10}, {IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "Green", &cv_grgammagreen, 20}, {IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "Blue", &cv_grgammablue, 30}, }; #endif static menuitem_t OP_SoundOptionsMenu[] = { {IT_STRING|IT_CVAR, NULL, "SFX", &cv_gamesounds, 10}, {IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "SFX Volume", &cv_soundvolume, 18}, {IT_STRING|IT_CVAR, NULL, "Music", &cv_gamedigimusic, 30}, {IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "Music Volume", &cv_digmusicvolume, 38}, /* -- :nonnathisshit: {IT_STRING|IT_CVAR, NULL, "MIDI", &cv_gamemidimusic, 50}, {IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "MIDI Volume", &cv_midimusicvolume, 58}, #ifdef PC_DOS {IT_STRING|IT_CVAR|IT_CV_SLIDER, NULL, "CD Volume", &cd_volume, 40}, #endif*/ //{IT_STRING|IT_CALL, NULL, "Restart Audio System", M_RestartAudio, 50}, {IT_STRING|IT_CVAR, NULL, "Reverse L/R Channels", &stereoreverse, 50}, {IT_STRING|IT_CVAR, NULL, "Surround Sound", &surround, 60}, {IT_STRING|IT_CVAR, NULL, "Chat Notifications", &cv_chatnotifications, 75}, {IT_STRING|IT_CVAR, NULL, "Character voices", &cv_kartvoices, 85}, {IT_STRING|IT_CVAR, NULL, "Powerup Warning", &cv_kartinvinsfx, 95}, {IT_KEYHANDLER|IT_STRING, NULL, "Sound Test", M_HandleSoundTest, 110}, {IT_STRING|IT_CVAR, NULL, "Play Music While Unfocused", &cv_playmusicifunfocused, 125}, {IT_STRING|IT_CVAR, NULL, "Play SFX While Unfocused", &cv_playsoundifunfocused, 135}, }; /*static menuitem_t OP_DataOptionsMenu[] = { {IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 10}, {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 30}, };*/ static menuitem_t OP_ScreenshotOptionsMenu[] = { {IT_STRING|IT_CVAR, NULL, "Storage Location", &cv_screenshot_option, 10}, {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_screenshot_folder, 20}, {IT_HEADER, NULL, "Screenshots (F8)", NULL, 50}, {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memory, 60}, {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_level, 70}, {IT_STRING|IT_CVAR, NULL, "Strategy", &cv_zlib_strategy, 80}, {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bits, 90}, {IT_HEADER, NULL, "Movie Mode (F9)", NULL, 105}, {IT_STRING|IT_CVAR, NULL, "Capture Mode", &cv_moviemode, 115}, {IT_STRING|IT_CVAR, NULL, "Region Optimizing", &cv_gif_optimize, 125}, {IT_STRING|IT_CVAR, NULL, "Downscaling", &cv_gif_downscale, 135}, {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memorya, 125}, {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_levela, 135}, {IT_STRING|IT_CVAR, NULL, "Strategy", &cv_zlib_strategya, 145}, {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bitsa, 155}, }; enum { op_screenshot_folder = 1, op_screenshot_capture = 8, op_screenshot_gif_start = 9, op_screenshot_gif_end = 10, op_screenshot_apng_start = 11, op_screenshot_apng_end = 14, }; static menuitem_t OP_EraseDataMenu[] = { {IT_STRING | IT_CALL, NULL, "Erase Record Data", M_EraseData, 10}, {IT_STRING | IT_CALL, NULL, "Erase Unlockable Data", M_EraseData, 20}, {IT_STRING | IT_CALL, NULL, "\x85" "Erase ALL Data", M_EraseData, 40}, }; static menuitem_t OP_AddonsOptionsMenu[] = { {IT_HEADER, NULL, "Menu", NULL, 0}, {IT_STRING|IT_CVAR, NULL, "Location", &cv_addons_option, 10}, {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_addons_folder, 20}, {IT_STRING|IT_CVAR, NULL, "Identify add-ons via", &cv_addons_md5, 48}, {IT_STRING|IT_CVAR, NULL, "Show unsupported file types", &cv_addons_showall, 58}, {IT_HEADER, NULL, "Search", NULL, 76}, {IT_STRING|IT_CVAR, NULL, "Matching", &cv_addons_search_type, 86}, {IT_STRING|IT_CVAR, NULL, "Case-sensitive", &cv_addons_search_case, 96}, }; enum { op_addons_folder = 2, }; static menuitem_t OP_HUDOptionsMenu[] = { {IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 10}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "HUD Visibility", &cv_translucenthud, 20}, {IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 35}, {IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 45}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Minimap Visibility", &cv_kartminimap, 60}, {IT_STRING | IT_CVAR, NULL, "Speedometer Display", &cv_kartspeedometer, 70}, {IT_STRING | IT_CVAR, NULL, "Show \"CHECK\"", &cv_kartcheck, 80}, {IT_STRING | IT_CVAR, NULL, "Menu Highlights", &cons_menuhighlight, 95}, // highlight info - (GOOD HIGHLIGHT, WARNING HIGHLIGHT) - 105 (see M_DrawHUDOptions) {IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 120}, {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 135}, }; // Ok it's still called chatoptions but we'll put ping display in here to be clean static menuitem_t OP_ChatOptionsMenu[] = { // will ANYONE who doesn't know how to use the console want to touch this one? {IT_STRING | IT_CVAR, NULL, "Chat Mode", &cv_consolechat, 10}, // nonetheless... {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Width", &cv_chatwidth, 25}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Height", &cv_chatheight, 35}, {IT_STRING | IT_CVAR, NULL, "Chat Background Tint", &cv_chatbacktint, 50}, {IT_STRING | IT_CVAR, NULL, "Message Fadeout Time", &cv_chattime, 60}, {IT_STRING | IT_CVAR, NULL, "Spam Protection", &cv_chatspamprotection, 70}, {IT_STRING | IT_CVAR, NULL, "Local ping display", &cv_showping, 90}, // shows ping next to framerate if we want to. }; static menuitem_t OP_GameOptionsMenu[] = { {IT_STRING | IT_SUBMENU, NULL, "Random Item Toggles...", &OP_MonitorToggleDef, 10}, {IT_STRING | IT_CVAR, NULL, "Game Speed", &cv_kartspeed, 30}, {IT_STRING | IT_CVAR, NULL, "Frantic Items", &cv_kartfrantic, 40}, {IT_SECRET, NULL, "Encore Mode", &cv_kartencore, 50}, {IT_STRING | IT_CVAR, NULL, "Number of Laps", &cv_basenumlaps, 70}, {IT_STRING | IT_CVAR, NULL, "Exit Countdown Timer", &cv_countdowntime, 80}, {IT_STRING | IT_CVAR, NULL, "Time Limit", &cv_timelimit, 100}, {IT_STRING | IT_CVAR, NULL, "Starting Bumpers", &cv_kartbumpers, 110}, {IT_STRING | IT_CVAR, NULL, "Karma Comeback", &cv_kartcomeback, 120}, {IT_STRING | IT_CVAR, NULL, "Force Character", &cv_forceskin, 140}, {IT_STRING | IT_CVAR, NULL, "Restrict Character Changes", &cv_restrictskinchange, 150}, }; static menuitem_t OP_ServerOptionsMenu[] = { #ifndef NONET {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Server Name", &cv_servername, 10}, #endif {IT_STRING | IT_CVAR, NULL, "Intermission Timer", &cv_inttime, 40}, {IT_STRING | IT_CVAR, NULL, "Map Progression", &cv_advancemap, 50}, {IT_STRING | IT_CVAR, NULL, "Voting Timer", &cv_votetime, 60}, {IT_STRING | IT_CVAR, NULL, "Voting Rule Changes", &cv_kartvoterulechanges, 70}, #ifndef NONET {IT_STRING | IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 90}, {IT_STRING | IT_CVAR, NULL, "Allow Players to Join", &cv_allownewplayer, 100}, {IT_STRING | IT_CVAR, NULL, "Allow Add-on Downloading", &cv_downloading, 110}, {IT_STRING | IT_CVAR, NULL, "Pause Permission", &cv_pause, 120}, {IT_STRING | IT_CVAR, NULL, "Mute All Chat", &cv_mute, 130}, {IT_SUBMENU|IT_STRING, NULL, "Advanced Options...", &OP_AdvServerOptionsDef,150}, #endif }; #ifndef NONET static menuitem_t OP_AdvServerOptionsMenu[] = { {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Server Browser Address", &cv_masterserver, 10}, {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 40}, {IT_STRING | IT_CVAR, NULL, "Ping limit (ms)", &cv_maxping, 50}, {IT_STRING | IT_CVAR, NULL, "Ping timeout (s)", &cv_pingtimeout, 60}, {IT_STRING | IT_CVAR, NULL, "Connection timeout (tics)", &cv_nettimeout, 70}, {IT_STRING | IT_CVAR, NULL, "Join timeout (tics)", &cv_jointimeout, 80}, {IT_STRING | IT_CVAR, NULL, "Max. file transfer send (KB)", &cv_maxsend, 100}, {IT_STRING | IT_CVAR, NULL, "File transfer packet rate", &cv_downloadspeed, 110}, {IT_STRING | IT_CVAR, NULL, "Log join addresses", &cv_showjoinaddress, 130}, {IT_STRING | IT_CVAR, NULL, "Log resyncs", &cv_blamecfail, 140}, {IT_STRING | IT_CVAR, NULL, "Log file transfers", &cv_noticedownload, 150}, }; #endif /*static menuitem_t OP_NetgameOptionsMenu[] = { {IT_STRING | IT_CVAR, NULL, "Time Limit", &cv_timelimit, 10}, {IT_STRING | IT_CVAR, NULL, "Point Limit", &cv_pointlimit, 18}, {IT_STRING | IT_CVAR, NULL, "Frantic Items", &cv_kartfrantic, 34}, {IT_STRING | IT_CVAR, NULL, "Item Respawn", &cv_itemrespawn, 50}, {IT_STRING | IT_CVAR, NULL, "Item Respawn Delay", &cv_itemrespawntime, 58}, {IT_STRING | IT_CVAR, NULL, "Player Respawn Delay", &cv_respawntime, 74}, {IT_STRING | IT_CVAR, NULL, "Force Skin #", &cv_forceskin, 90}, {IT_STRING | IT_CVAR, NULL, "Restrict Skin Changes", &cv_restrictskinchange, 98}, //{IT_STRING | IT_CVAR, NULL, "Autobalance Teams", &cv_autobalance, 114}, //{IT_STRING | IT_CVAR, NULL, "Scramble Teams on Map Change", &cv_scrambleonchange, 122}, };*/ /*static menuitem_t OP_GametypeOptionsMenu[] = { {IT_HEADER, NULL, "RACE", NULL, 2}, {IT_STRING | IT_CVAR, NULL, "Game Speed", &cv_kartspeed, 10}, {IT_STRING | IT_CVAR, NULL, "Encore Mode", &cv_kartencore, 18}, {IT_STRING | IT_CVAR, NULL, "Number of Laps", &cv_numlaps, 26}, {IT_STRING | IT_CVAR, NULL, "Use Map Lap Counts", &cv_usemapnumlaps, 34}, {IT_HEADER, NULL, "BATTLE", NULL, 50}, {IT_STRING | IT_CVAR, NULL, "Starting Bumpers", &cv_kartbumpers, 58}, {IT_STRING | IT_CVAR, NULL, "Karma Comeback", &cv_kartcomeback, 66}, };*/ #define ITEMTOGGLEBOTTOMRIGHT static menuitem_t OP_MonitorToggleMenu[] = { // Mostly handled by the drawing function. // Instead of using this for dumb monitors, lets use the new item bools we have :V {IT_KEYHANDLER | IT_NOTHING, NULL, "Sneakers", M_HandleMonitorToggles, KITEM_SNEAKER}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Sneakers x3", M_HandleMonitorToggles, KRITEM_TRIPLESNEAKER}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Rocket Sneakers", M_HandleMonitorToggles, KITEM_ROCKETSNEAKER}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Toggle All", M_HandleMonitorToggles, 0}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas", M_HandleMonitorToggles, KITEM_BANANA}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x3", M_HandleMonitorToggles, KRITEM_TRIPLEBANANA}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Bananas x10", M_HandleMonitorToggles, KRITEM_TENFOLDBANANA}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Eggman Monitors", M_HandleMonitorToggles, KITEM_EGGMAN}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts", M_HandleMonitorToggles, KITEM_ORBINAUT}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts x3", M_HandleMonitorToggles, KRITEM_TRIPLEORBINAUT}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Orbinauts x4", M_HandleMonitorToggles, KRITEM_QUADORBINAUT}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Mines", M_HandleMonitorToggles, KITEM_MINE}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Jawz", M_HandleMonitorToggles, KITEM_JAWZ}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Jawz x2", M_HandleMonitorToggles, KRITEM_DUALJAWZ}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Ballhogs", M_HandleMonitorToggles, KITEM_BALLHOG}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Self-Propelled Bombs", M_HandleMonitorToggles, KITEM_SPB}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Invinciblity", M_HandleMonitorToggles, KITEM_INVINCIBILITY}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Grow", M_HandleMonitorToggles, KITEM_GROW}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Shrink", M_HandleMonitorToggles, KITEM_SHRINK}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Thunder Shields", M_HandleMonitorToggles, KITEM_THUNDERSHIELD}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Hyudoros", M_HandleMonitorToggles, KITEM_HYUDORO}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Pogo Springs", M_HandleMonitorToggles, KITEM_POGOSPRING}, {IT_KEYHANDLER | IT_NOTHING, NULL, "Kitchen Sinks", M_HandleMonitorToggles, KITEM_KITCHENSINK}, #ifdef ITEMTOGGLEBOTTOMRIGHT {IT_KEYHANDLER | IT_NOTHING, NULL, "---", M_HandleMonitorToggles, 255}, #endif }; // ========================================================================== // ALL MENU DEFINITIONS GO HERE // ========================================================================== // Main Menu and related menu_t MainDef = CENTERMENUSTYLE(NULL, MainMenu, NULL, 72); menu_t MISC_AddonsDef = { NULL, sizeof (MISC_AddonsMenu)/sizeof (menuitem_t), &MainDef, MISC_AddonsMenu, M_DrawAddons, 50, 28, 0, NULL }; menu_t MISC_ReplayHutDef = { NULL, sizeof (MISC_ReplayHutMenu)/sizeof (menuitem_t), NULL, MISC_ReplayHutMenu, M_DrawReplayHut, 30, 80, (sizeof (MISC_ReplayHutMenu)/sizeof (menuitem_t)) - 2, // Start on the replay list M_QuitReplayHut }; menu_t MISC_ReplayOptionsDef = { NULL, sizeof (MISC_ReplayOptionsMenu)/sizeof (menuitem_t), &MISC_ReplayHutDef, MISC_ReplayOptionsMenu, M_DrawGenericBackgroundMenu, 27, 40, 0, NULL }; menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72); menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72); menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); // Misc Main Menu menu_t MISC_ScrambleTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ScrambleTeamMenu, &MPauseDef, 27, 40); menu_t MISC_ChangeTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ChangeTeamMenu, &MPauseDef, 27, 40); menu_t MISC_ChangeSpectateDef = DEFAULTMENUSTYLE(NULL, MISC_ChangeSpectateMenu, &MPauseDef, 27, 40); menu_t MISC_ChangeLevelDef = MAPICONMENUSTYLE(NULL, MISC_ChangeLevelMenu, &MPauseDef); menu_t MISC_HelpDef = IMAGEDEF(MISC_HelpMenu); // // M_GetGametypeColor // // Pretty and consistent ^u^ // See also G_GetGametypeColor. // static INT32 highlightflags, recommendedflags, warningflags; inline static void M_GetGametypeColor(void) { INT16 gt; warningflags = V_REDMAP; recommendedflags = V_GREENMAP; if (cons_menuhighlight.value) { highlightflags = cons_menuhighlight.value; if (highlightflags == V_REDMAP) { warningflags = V_ORANGEMAP; return; } if (highlightflags == V_GREENMAP) { recommendedflags = V_SKYMAP; return; } return; } warningflags = V_REDMAP; recommendedflags = V_GREENMAP; if (modeattacking // == ATTACKING_RECORD || gamestate == GS_TIMEATTACK) { highlightflags = V_ORANGEMAP; return; } if (currentMenu->drawroutine == M_DrawServerMenu) gt = cv_newgametype.value; else if (!Playing()) { highlightflags = V_YELLOWMAP; return; } else gt = gametype; if (gt == GT_MATCH) { highlightflags = V_REDMAP; warningflags = V_ORANGEMAP; return; } if (gt == GT_RACE) { highlightflags = V_SKYMAP; return; } highlightflags = V_YELLOWMAP; // FALLBACK } // excuse me but I'm extremely lazy: INT32 HU_GetHighlightColor(void) { M_GetGametypeColor(); // update flag colour reguardless of the menu being opened or not. return highlightflags; } // Sky Room menu_t SR_PandoraDef = { "M_PANDRA", sizeof (SR_PandorasBox)/sizeof (menuitem_t), &SPauseDef, SR_PandorasBox, M_DrawGenericMenu, 60, 40, 0, M_ExitPandorasBox }; menu_t SR_MainDef = { "M_SECRET", sizeof (SR_MainMenu)/sizeof (menuitem_t), &MainDef, SR_MainMenu, M_DrawGenericMenu, //M_DrawSkyRoom, 60, 40, 0, NULL }; //menu_t SR_LevelSelectDef = MAPICONMENUSTYLE(NULL, SR_LevelSelectMenu, &SR_MainDef); menu_t SR_UnlockChecklistDef = { NULL, 1, &SR_MainDef, SR_UnlockChecklistMenu, M_DrawChecklist, 280, 185, 0, NULL }; menu_t SR_EmblemHintDef = { NULL, sizeof (SR_EmblemHintMenu)/sizeof (menuitem_t), &SPauseDef, SR_EmblemHintMenu, M_DrawEmblemHints, 60, 150, 0, NULL }; // Single Player menu_t SP_MainDef = CENTERMENUSTYLE(NULL, SP_MainMenu, &MainDef, 72); /*menu_t SP_LoadDef = { "M_PICKG", 1, &SP_MainDef, SP_LoadGameMenu, M_DrawLoad, 68, 46, 0, NULL }; menu_t SP_LevelSelectDef = MAPICONMENUSTYLE(NULL, SP_LevelSelectMenu, &SP_LoadDef);*/ menu_t SP_LevelStatsDef = { "M_STATS", 1, &SR_MainDef, SP_LevelStatsMenu, M_DrawLevelStats, 280, 185, 0, NULL }; static menu_t SP_TimeAttackDef = { "M_ATTACK", sizeof (SP_TimeAttackMenu)/sizeof (menuitem_t), &MainDef, // Doesn't matter. SP_TimeAttackMenu, M_DrawTimeAttackMenu, 34, 40, 0, M_QuitTimeAttackMenu }; static menu_t SP_ReplayDef = { "M_ATTACK", sizeof(SP_ReplayMenu)/sizeof(menuitem_t), &SP_TimeAttackDef, SP_ReplayMenu, M_DrawTimeAttackMenu, 34, 40, 0, NULL }; static menu_t SP_GuestReplayDef = { "M_ATTACK", sizeof(SP_GuestReplayMenu)/sizeof(menuitem_t), &SP_TimeAttackDef, SP_GuestReplayMenu, M_DrawTimeAttackMenu, 34, 40, 0, NULL }; static menu_t SP_GhostDef = { "M_ATTACK", sizeof(SP_GhostMenu)/sizeof(menuitem_t), &SP_TimeAttackDef, SP_GhostMenu, M_DrawTimeAttackMenu, 34, 40, 0, NULL }; /*static menu_t SP_NightsAttackDef = { "M_NIGHTS", sizeof (SP_NightsAttackMenu)/sizeof (menuitem_t), &MainDef, // Doesn't matter. SP_NightsAttackMenu, M_DrawNightsAttackMenu, 32, 40, 0, NULL }; static menu_t SP_NightsReplayDef = { "M_NIGHTS", sizeof(SP_NightsReplayMenu)/sizeof(menuitem_t), &SP_NightsAttackDef, SP_NightsReplayMenu, M_DrawNightsAttackMenu, 32, 120, 0, NULL }; static menu_t SP_NightsGuestReplayDef = { "M_NIGHTS", sizeof(SP_NightsGuestReplayMenu)/sizeof(menuitem_t), &SP_NightsAttackDef, SP_NightsGuestReplayMenu, M_DrawNightsAttackMenu, 32, 120, 0, NULL }; static menu_t SP_NightsGhostDef = { "M_NIGHTS", sizeof(SP_NightsGhostMenu)/sizeof(menuitem_t), &SP_NightsAttackDef, SP_NightsGhostMenu, M_DrawNightsAttackMenu, 32, 120, 0, NULL };*/ /*menu_t SP_PlayerDef = { "M_PICKP", sizeof (PlayerMenu)/sizeof (menuitem_t),//player_end, &SP_MainDef, PlayerMenu, M_DrawSetupChoosePlayerMenu, 24, 32, 0, NULL };*/ // Multiplayer menu_t MP_MainDef = { "M_MULTI", sizeof (MP_MainMenu)/sizeof (menuitem_t), &MainDef, MP_MainMenu, M_DrawMPMainMenu, 42, 30, 0, #ifndef NONET M_CancelConnect #else NULL #endif }; menu_t MP_OfflineServerDef = MAPICONMENUSTYLE("M_MULTI", MP_OfflineServerMenu, &MP_MainDef); #ifndef NONET menu_t MP_ServerDef = MAPICONMENUSTYLE("M_MULTI", MP_ServerMenu, &MP_MainDef); menu_t MP_ConnectDef = { "M_MULTI", sizeof (MP_ConnectMenu)/sizeof (menuitem_t), &MP_MainDef, MP_ConnectMenu, M_DrawConnectMenu, 27,24, 0, M_CancelConnect }; menu_t MP_RoomDef = { "M_MULTI", sizeof (MP_RoomMenu)/sizeof (menuitem_t), &MP_ConnectDef, MP_RoomMenu, M_DrawRoomMenu, 27, 32, 0, NULL }; #endif menu_t MP_PlayerSetupDef = { NULL, //"M_SPLAYR" sizeof (MP_PlayerSetupMenu)/sizeof (menuitem_t), &MP_MainDef, MP_PlayerSetupMenu, M_DrawSetupMultiPlayerMenu, 36, 14, 0, M_QuitMultiPlayerMenu }; // Options menu_t OP_MainDef = { "M_OPTTTL", sizeof (OP_MainMenu)/sizeof (menuitem_t), &MainDef, OP_MainMenu, M_DrawGenericMenu, 60, 30, 0, NULL }; menu_t OP_ControlsDef = DEFAULTMENUSTYLE("M_CONTRO", OP_ControlsMenu, &OP_MainDef, 60, 30); menu_t OP_AllControlsDef = CONTROLMENUSTYLE(OP_AllControlsMenu, &OP_ControlsDef); menu_t OP_Joystick1Def = DEFAULTMENUSTYLE("M_CONTRO", OP_Joystick1Menu, &OP_AllControlsDef, 60, 30); menu_t OP_Joystick2Def = DEFAULTMENUSTYLE("M_CONTRO", OP_Joystick2Menu, &OP_AllControlsDef, 60, 30); menu_t OP_Joystick3Def = DEFAULTMENUSTYLE("M_CONTRO", OP_Joystick3Menu, &OP_AllControlsDef, 60, 30); menu_t OP_Joystick4Def = DEFAULTMENUSTYLE("M_CONTRO", OP_Joystick4Menu, &OP_AllControlsDef, 60, 30); menu_t OP_JoystickSetDef = { "M_CONTRO", sizeof (OP_JoystickSetMenu)/sizeof (menuitem_t), &OP_Joystick1Def, OP_JoystickSetMenu, M_DrawJoystick, 50, 40, 0, NULL }; menu_t OP_VideoOptionsDef = { "M_VIDEO", sizeof(OP_VideoOptionsMenu)/sizeof(menuitem_t), &OP_MainDef, OP_VideoOptionsMenu, M_DrawVideoMenu, 30, 30, 0, NULL }; menu_t OP_VideoModeDef = { "M_VIDEO", 1, &OP_VideoOptionsDef, OP_VideoModeMenu, M_DrawVideoMode, 48, 26, 0, NULL }; menu_t OP_SoundOptionsDef = { "M_SOUND", sizeof (OP_SoundOptionsMenu)/sizeof (menuitem_t), &OP_MainDef, OP_SoundOptionsMenu, M_DrawSkyRoom, 30, 30, 0, NULL }; menu_t OP_HUDOptionsDef = { "M_HUD", sizeof (OP_HUDOptionsMenu)/sizeof (menuitem_t), &OP_MainDef, OP_HUDOptionsMenu, M_DrawHUDOptions, 30, 30, 0, NULL }; menu_t OP_ChatOptionsDef = DEFAULTMENUSTYLE("M_HUD", OP_ChatOptionsMenu, &OP_HUDOptionsDef, 30, 30); menu_t OP_GameOptionsDef = DEFAULTMENUSTYLE("M_GAME", OP_GameOptionsMenu, &OP_MainDef, 30, 30); menu_t OP_ServerOptionsDef = DEFAULTMENUSTYLE("M_SERVER", OP_ServerOptionsMenu, &OP_MainDef, 24, 30); #ifndef NONET menu_t OP_AdvServerOptionsDef = DEFAULTMENUSTYLE("M_SERVER", OP_AdvServerOptionsMenu, &OP_ServerOptionsDef, 24, 30); #endif //menu_t OP_NetgameOptionsDef = DEFAULTMENUSTYLE("M_SERVER", OP_NetgameOptionsMenu, &OP_ServerOptionsDef, 30, 30); //menu_t OP_GametypeOptionsDef = DEFAULTMENUSTYLE("M_SERVER", OP_GametypeOptionsMenu, &OP_ServerOptionsDef, 30, 30); //menu_t OP_ChatOptionsDef = DEFAULTMENUSTYLE("M_GAME", OP_ChatOptionsMenu, &OP_GameOptionsDef, 30, 30); menu_t OP_MonitorToggleDef = { "M_GAME", sizeof (OP_MonitorToggleMenu)/sizeof (menuitem_t), &OP_GameOptionsDef, OP_MonitorToggleMenu, M_DrawMonitorToggles, 47, 30, 0, NULL }; #ifdef HWRENDER menu_t OP_OpenGLOptionsDef = DEFAULTMENUSTYLE("M_VIDEO", OP_OpenGLOptionsMenu, &OP_VideoOptionsDef, 30, 30); #ifdef ALAM_LIGHTING menu_t OP_OpenGLLightingDef = DEFAULTMENUSTYLE("M_VIDEO", OP_OpenGLLightingMenu, &OP_OpenGLOptionsDef, 60, 40); #endif menu_t OP_OpenGLFogDef = { "M_VIDEO", sizeof (OP_OpenGLFogMenu)/sizeof (menuitem_t), &OP_OpenGLOptionsDef, OP_OpenGLFogMenu, M_OGL_DrawFogMenu, 60, 40, 0, NULL }; menu_t OP_OpenGLColorDef = { "M_VIDEO", sizeof (OP_OpenGLColorMenu)/sizeof (menuitem_t), &OP_OpenGLOptionsDef, OP_OpenGLColorMenu, M_OGL_DrawColorMenu, 60, 40, 0, NULL }; #endif //menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE("M_DATA", OP_DataOptionsMenu, &OP_MainDef, 60, 30); menu_t OP_ScreenshotOptionsDef = DEFAULTMENUSTYLE("M_SCSHOT", OP_ScreenshotOptionsMenu, &OP_MainDef, 30, 30); menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_MainDef, 30, 30); menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_MainDef, 30, 30); // ========================================================================== // CVAR ONCHANGE EVENTS GO HERE // ========================================================================== // (there's only a couple anyway) // Prototypes static INT32 M_FindFirstMap(INT32 gtype); static INT32 M_GetFirstLevelInList(void); // Nextmap. Used for Time Attack. static void Nextmap_OnChange(void) { char *leveltitle; UINT8 active; // Update the string in the consvar. Z_Free(cv_nextmap.zstring); leveltitle = G_BuildMapTitle(cv_nextmap.value); cv_nextmap.string = cv_nextmap.zstring = leveltitle ? leveltitle : Z_StrDup(G_BuildMapName(cv_nextmap.value)); if (currentMenu == &SP_TimeAttackDef) { // see also p_setup.c's P_LoadRecordGhosts const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; char *gpath = malloc(glen); INT32 i; if (!gpath) return; sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); CV_StealthSetValue(&cv_dummystaff, 0); active = false; SP_TimeAttackMenu[taguest].status = IT_DISABLED; SP_TimeAttackMenu[tareplay].status = IT_DISABLED; //SP_TimeAttackMenu[taghost].status = IT_DISABLED; // Check if file exists, if not, disable REPLAY option for (i = 0; i < 4; i++) { SP_ReplayMenu[i].status = IT_DISABLED; SP_GuestReplayMenu[i].status = IT_DISABLED; } SP_ReplayMenu[4].status = IT_DISABLED; SP_GhostMenu[3].status = IT_DISABLED; SP_GhostMenu[4].status = IT_DISABLED; if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, cv_chooseskin.string))) { SP_ReplayMenu[0].status = IT_WHITESTRING|IT_CALL; SP_GuestReplayMenu[0].status = IT_WHITESTRING|IT_CALL; active |= 3; } if (FIL_FileExists(va("%s-%s-lap-best.lmp", gpath, cv_chooseskin.string))) { SP_ReplayMenu[1].status = IT_WHITESTRING|IT_CALL; SP_GuestReplayMenu[1].status = IT_WHITESTRING|IT_CALL; active |= 3; } if (FIL_FileExists(va("%s-%s-last.lmp", gpath, cv_chooseskin.string))) { SP_ReplayMenu[2].status = IT_WHITESTRING|IT_CALL; SP_GuestReplayMenu[2].status = IT_WHITESTRING|IT_CALL; active |= 3; } if (FIL_FileExists(va("%s-guest.lmp", gpath))) { SP_ReplayMenu[3].status = IT_WHITESTRING|IT_CALL; SP_GuestReplayMenu[3].status = IT_WHITESTRING|IT_CALL; SP_GhostMenu[3].status = IT_STRING|IT_CVAR; active |= 3; } CV_SetValue(&cv_dummystaff, 1); if (cv_dummystaff.value) { SP_ReplayMenu[4].status = IT_WHITESTRING|IT_KEYHANDLER; SP_GhostMenu[4].status = IT_STRING|IT_CVAR; CV_StealthSetValue(&cv_dummystaff, 1); active |= 1; } if (active) { if (active & 1) SP_TimeAttackMenu[tareplay].status = IT_WHITESTRING|IT_SUBMENU; if (active & 2) SP_TimeAttackMenu[taguest].status = IT_WHITESTRING|IT_SUBMENU; } else if (itemOn == tareplay) // Reset lastOn so replay isn't still selected when not available. { currentMenu->lastOn = itemOn; itemOn = tastart; } if (mapheaderinfo[cv_nextmap.value-1] && mapheaderinfo[cv_nextmap.value-1]->forcecharacter[0] != '\0') CV_Set(&cv_chooseskin, mapheaderinfo[cv_nextmap.value-1]->forcecharacter); free(gpath); } } static void Dummymenuplayer_OnChange(void) { if (cv_dummymenuplayer.value < 1) CV_StealthSetValue(&cv_dummymenuplayer, splitscreen+1); else if (cv_dummymenuplayer.value > splitscreen+1) CV_StealthSetValue(&cv_dummymenuplayer, 1); } /*static void Dummymares_OnChange(void) { if (!nightsrecords[cv_nextmap.value-1]) { CV_StealthSetValue(&cv_dummymares, 0); return; } else { UINT8 mares = nightsrecords[cv_nextmap.value-1]->nummares; if (cv_dummymares.value < 0) CV_StealthSetValue(&cv_dummymares, mares); else if (cv_dummymares.value > mares) CV_StealthSetValue(&cv_dummymares, 0); } }*/ char dummystaffname[22]; static void Dummystaff_OnChange(void) { lumpnum_t l; dummystaffname[0] = '\0'; if ((l = W_CheckNumForName(va("%sS01",G_BuildMapName(cv_nextmap.value)))) == LUMPERROR) { CV_StealthSetValue(&cv_dummystaff, 0); return; } else { char *temp = dummystaffname; UINT8 numstaff = 1; while (numstaff < 99 && (l = W_CheckNumForName(va("%sS%02u",G_BuildMapName(cv_nextmap.value),numstaff+1))) != LUMPERROR) numstaff++; if (cv_dummystaff.value < 1) CV_StealthSetValue(&cv_dummystaff, numstaff); else if (cv_dummystaff.value > numstaff) CV_StealthSetValue(&cv_dummystaff, 1); if ((l = W_CheckNumForName(va("%sS%02u",G_BuildMapName(cv_nextmap.value), cv_dummystaff.value))) == LUMPERROR) return; // shouldn't happen but might as well check... G_UpdateStaffGhostName(l); while (*temp) temp++; sprintf(temp, " - %d", cv_dummystaff.value); } } // Newgametype. Used for gametype changes. static void Newgametype_OnChange(void) { if (cv_nextmap.value && menuactive) { if (!mapheaderinfo[cv_nextmap.value-1]) P_AllocMapHeader((INT16)(cv_nextmap.value-1)); if ((cv_newgametype.value == GT_RACE && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_RACE)) || // SRB2kart //(cv_newgametype.value == GT_COMPETITION && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_COMPETITION)) || //(cv_newgametype.value == GT_RACE && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_RACE)) || ((cv_newgametype.value == GT_MATCH || cv_newgametype.value == GT_TEAMMATCH) && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_MATCH))) // || //((cv_newgametype.value == GT_TAG || cv_newgametype.value == GT_HIDEANDSEEK) && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_TAG)) || //(cv_newgametype.value == GT_CTF && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_CTF))) { INT32 value = 0; switch (cv_newgametype.value) { case GT_COOP: value = TOL_RACE; // SRB2kart break; case GT_COMPETITION: value = TOL_COMPETITION; break; case GT_RACE: value = TOL_RACE; break; case GT_MATCH: case GT_TEAMMATCH: value = TOL_MATCH; break; case GT_TAG: case GT_HIDEANDSEEK: value = TOL_TAG; break; case GT_CTF: value = TOL_CTF; break; } CV_SetValue(&cv_nextmap, M_FindFirstMap(value)); //CV_AddValue(&cv_nextmap, -1); //CV_AddValue(&cv_nextmap, 1); } } } void Screenshot_option_Onchange(void) { OP_ScreenshotOptionsMenu[op_screenshot_folder].status = (cv_screenshot_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); } void Moviemode_mode_Onchange(void) { INT32 i, cstart, cend; for (i = op_screenshot_gif_start; i <= op_screenshot_apng_end; ++i) OP_ScreenshotOptionsMenu[i].status = IT_DISABLED; switch (cv_moviemode.value) { case MM_GIF: cstart = op_screenshot_gif_start; cend = op_screenshot_gif_end; break; case MM_APNG: cstart = op_screenshot_apng_start; cend = op_screenshot_apng_end; break; default: return; } for (i = cstart; i <= cend; ++i) OP_ScreenshotOptionsMenu[i].status = IT_STRING|IT_CVAR; } void Addons_option_Onchange(void) { OP_AddonsOptionsMenu[op_addons_folder].status = (cv_addons_option.value == 3 ? IT_CVAR|IT_STRING|IT_CV_STRING : IT_DISABLED); } // ========================================================================== // END ORGANIZATION STUFF. // ========================================================================== // current menudef menu_t *currentMenu = &MainDef; // ========================================================================= // BASIC MENU HANDLING // ========================================================================= static void M_ChangeCvar(INT32 choice) { consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction; if (choice == -1) { if (cv == &cv_playercolor) { SINT8 skinno = R_SkinAvailable(cv_chooseskin.string); if (skinno != -1) CV_SetValue(cv,skins[skinno].prefcolor); return; } CV_Set(cv,cv->defaultvalue); return; } choice = (choice<<1) - 1; if (((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_SLIDER) ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_INVISSLIDER) ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_NOMOD)) { CV_SetValue(cv,cv->value+choice); } else if (cv->flags & CV_FLOAT) { char s[20]; sprintf(s,"%f",FIXED_TO_FLOAT(cv->value)+(choice)*(1.0f/16.0f)); CV_Set(cv,s); } else { #ifndef NONET if (cv == &cv_nettimeout || cv == &cv_jointimeout) choice *= (TICRATE/7); else if (cv == &cv_maxsend) choice *= 512; #ifdef NEWPING else if (cv == &cv_maxping) choice *= 50; #endif #endif CV_AddValue(cv,choice); } } static boolean M_ChangeStringCvar(INT32 choice) { consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction; char buf[MAXSTRINGLENGTH]; size_t len; if (shiftdown && choice >= 32 && choice <= 127) choice = shiftxform[choice]; switch (choice) { case KEY_BACKSPACE: len = strlen(cv->string); if (len > 0) { S_StartSound(NULL,sfx_menu1); // Tails M_Memcpy(buf, cv->string, len); buf[len-1] = 0; CV_Set(cv, buf); } return true; case KEY_DEL: if (cv->string[0]) { S_StartSound(NULL,sfx_menu1); // Tails CV_Set(cv, ""); } return true; default: if (choice >= 32 && choice <= 127) { len = strlen(cv->string); if (len < MAXSTRINGLENGTH - 1) { S_StartSound(NULL,sfx_menu1); // Tails M_Memcpy(buf, cv->string, len); buf[len++] = (char)choice; buf[len] = 0; CV_Set(cv, buf); } return true; } break; } return false; } static void M_NextOpt(void) { INT16 oldItemOn = itemOn; // prevent infinite loop if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD) ((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value = 0; do { if (itemOn + 1 > currentMenu->numitems - 1) itemOn = 0; else itemOn++; } while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE); } static void M_PrevOpt(void) { INT16 oldItemOn = itemOn; // prevent infinite loop if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD) ((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value = 0; do { if (!itemOn) itemOn = currentMenu->numitems - 1; else itemOn--; } while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE); } // lock out further input in a tic when important buttons are pressed // (in other words -- stop bullshit happening by mashing buttons in fades) static boolean noFurtherInput = false; static void Command_Manual_f(void) { if (modeattacking) return; M_StartControlPanel(); M_Manual(INT32_MAX); itemOn = 0; } // // M_Responder // boolean M_Responder(event_t *ev) { INT32 ch = -1; // INT32 i; static tic_t joywait = 0, mousewait = 0; static INT32 pjoyx = 0, pjoyy = 0; static INT32 pmousex = 0, pmousey = 0; static INT32 lastx = 0, lasty = 0; void (*routine)(INT32 choice); // for some casting problem if (dedicated || (demo.playback && demo.title) || gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) return false; if (noFurtherInput) { // Ignore input after enter/escape/other buttons // (but still allow shift keyup so caps doesn't get stuck) return false; } else if (ev->type == ev_keydown) { ch = ev->data1; // added 5-2-98 remap virtual keys (mouse & joystick buttons) switch (ch) { case KEY_MOUSE1: //case KEY_JOY1: //case KEY_JOY1 + 2: ch = KEY_ENTER; break; /*case KEY_JOY1 + 3: // Brake can function as 'n' for message boxes now. ch = 'n'; break;*/ case KEY_MOUSE1 + 1: //case KEY_JOY1 + 1: ch = KEY_BACKSPACE; break; case KEY_HAT1: ch = KEY_UPARROW; break; case KEY_HAT1 + 1: ch = KEY_DOWNARROW; break; case KEY_HAT1 + 2: ch = KEY_LEFTARROW; break; case KEY_HAT1 + 3: ch = KEY_RIGHTARROW; break; } } else if (menuactive) { if (ev->type == ev_joystick && ev->data1 == 0 && joywait < I_GetTime()) { const INT32 jdeadzone = JOYAXISRANGE/4; if (ev->data3 != INT32_MAX) { if (Joystick.bGamepadStyle || abs(ev->data3) > jdeadzone) { if (ev->data3 < 0 && pjoyy >= 0) { ch = KEY_UPARROW; joywait = I_GetTime() + NEWTICRATE/7; } else if (ev->data3 > 0 && pjoyy <= 0) { ch = KEY_DOWNARROW; joywait = I_GetTime() + NEWTICRATE/7; } pjoyy = ev->data3; } else pjoyy = 0; } if (ev->data2 != INT32_MAX) { if (Joystick.bGamepadStyle || abs(ev->data2) > jdeadzone) { if (ev->data2 < 0 && pjoyx >= 0) { ch = KEY_LEFTARROW; joywait = I_GetTime() + NEWTICRATE/17; } else if (ev->data2 > 0 && pjoyx <= 0) { ch = KEY_RIGHTARROW; joywait = I_GetTime() + NEWTICRATE/17; } pjoyx = ev->data2; } else pjoyx = 0; } } else if (ev->type == ev_mouse && mousewait < I_GetTime()) { pmousey += ev->data3; if (pmousey < lasty-30) { ch = KEY_DOWNARROW; mousewait = I_GetTime() + NEWTICRATE/7; pmousey = lasty -= 30; } else if (pmousey > lasty + 30) { ch = KEY_UPARROW; mousewait = I_GetTime() + NEWTICRATE/7; pmousey = lasty += 30; } pmousex += ev->data2; if (pmousex < lastx - 30) { ch = KEY_LEFTARROW; mousewait = I_GetTime() + NEWTICRATE/7; pmousex = lastx -= 30; } else if (pmousex > lastx+30) { ch = KEY_RIGHTARROW; mousewait = I_GetTime() + NEWTICRATE/7; pmousex = lastx += 30; } } } else if (ev->type == ev_keydown) // Preserve event for other responders ch = ev->data1; if (ch == -1) return false; else if (ch == gamecontrol[gc_systemmenu][0] || ch == gamecontrol[gc_systemmenu][1]) // allow remappable ESC key ch = KEY_ESCAPE; else if ((ch == gamecontrol[gc_accelerate][0] || ch == gamecontrol[gc_accelerate][1]) && ch >= KEY_MOUSE1) ch = KEY_ENTER; // F-Keys if (!menuactive) { noFurtherInput = true; switch (ch) { case KEY_F1: // Help key Command_Manual_f(); return true; case KEY_F2: // Empty return true; case KEY_F3: // Toggle HUD CV_SetValue(&cv_showhud, !cv_showhud.value); return true; case KEY_F4: // Sound Volume if (modeattacking) return true; M_StartControlPanel(); M_Options(0); currentMenu = &OP_SoundOptionsDef; itemOn = 0; return true; #ifndef DC case KEY_F5: // Video Mode if (modeattacking) return true; M_StartControlPanel(); M_Options(0); M_VideoModeMenu(0); return true; #endif case KEY_F6: // Empty return true; case KEY_F7: // Options if (modeattacking) return true; M_StartControlPanel(); M_Options(0); M_SetupNextMenu(&OP_MainDef); return true; // Screenshots on F8 now handled elsewhere // Same with Moviemode on F9 case KEY_F10: // Quit SRB2 M_QuitSRB2(0); return true; case KEY_F11: // Gamma Level CV_AddValue(&cv_usegamma, 1); return true; // Spymode on F12 handled in game logic case KEY_ESCAPE: // Pop up menu if (chat_on) { HU_clearChatChars(); chat_on = false; } else M_StartControlPanel(); return true; } noFurtherInput = false; // turns out we didn't care return false; } if ((ch == gamecontrol[gc_brake][0] || ch == gamecontrol[gc_brake][1]) && ch >= KEY_MOUSE1) // do this here, otherwise brake opens the menu mid-game ch = KEY_ESCAPE; routine = currentMenu->menuitems[itemOn].itemaction; // Handle menuitems which need a specific key handling if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_KEYHANDLER) { if (shiftdown && ch >= 32 && ch <= 127) ch = shiftxform[ch]; routine(ch); return true; } if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) { if (currentMenu->menuitems[itemOn].alphaKey != MM_EVENTHANDLER) { if (ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER) { if (routine) routine(ch); M_StopMessage(0); noFurtherInput = true; return true; } return true; } else { // dirty hack: for customising controls, I want only buttons/keys, not moves if (ev->type == ev_mouse || ev->type == ev_mouse2 || ev->type == ev_joystick || ev->type == ev_joystick2 || ev->type == ev_joystick3 || ev->type == ev_joystick4) return true; if (routine) { void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction; otherroutine(ev); //Alam: what a hack } return true; } } // BP: one of the more big hack i have never made if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR) { if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING || (currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD) { if (ch == KEY_TAB && (currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD) ((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value ^= 1; if (shiftdown && ch >= 32 && ch <= 127) ch = shiftxform[ch]; if (M_ChangeStringCvar(ch)) return true; else routine = NULL; } else routine = M_ChangeCvar; } // Keys usable within menu switch (ch) { case KEY_DOWNARROW: M_NextOpt(); S_StartSound(NULL, sfx_menu1); /*if (currentMenu == &SP_PlayerDef) { Z_Free(char_notes); char_notes = NULL; }*/ return true; case KEY_UPARROW: M_PrevOpt(); S_StartSound(NULL, sfx_menu1); /*if (currentMenu == &SP_PlayerDef) { Z_Free(char_notes); char_notes = NULL; }*/ return true; case KEY_LEFTARROW: if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) { if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) S_StartSound(NULL, sfx_menu1); routine(0); } return true; case KEY_RIGHTARROW: if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) { if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) S_StartSound(NULL, sfx_menu1); routine(1); } return true; case KEY_ENTER: noFurtherInput = true; currentMenu->lastOn = itemOn; if (routine) { if (((currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_CALL || (currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_SUBMENU) && (currentMenu->menuitems[itemOn].status & IT_CALLTYPE)) { if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && majormods) { S_StartSound(NULL, sfx_menu1); M_StartMessage(M_GetText("This cannot be done with complex add-ons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING); return true; } } S_StartSound(NULL, sfx_menu1); switch (currentMenu->menuitems[itemOn].status & IT_TYPE) { case IT_CVAR: case IT_ARROWS: routine(1); // right arrow break; case IT_CALL: routine(itemOn); break; case IT_SUBMENU: currentMenu->lastOn = itemOn; M_SetupNextMenu((menu_t *)currentMenu->menuitems[itemOn].itemaction); break; } } return true; case KEY_ESCAPE: //case KEY_JOY1 + 2: noFurtherInput = true; currentMenu->lastOn = itemOn; if (currentMenu->prevMenu) { //If we entered the game search menu, but didn't enter a game, //make sure the game doesn't still think we're in a netgame. if (!Playing() && netgame && multiplayer) { MSCloseUDPSocket(); // Clean up so we can re-open the connection later. netgame = false; multiplayer = false; } if (currentMenu == &SP_TimeAttackDef) //|| currentMenu == &SP_NightsAttackDef { // D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate. menuactive = false; D_StartTitle(); } else M_SetupNextMenu(currentMenu->prevMenu); } else M_ClearMenus(true); return true; case KEY_BACKSPACE: if ((currentMenu->menuitems[itemOn].status) == IT_CONTROL) { // detach any keys associated with the game control G_ClearControlKeys(setupcontrols, currentMenu->menuitems[itemOn].alphaKey); S_StartSound(NULL, sfx_shldls); return true; } if (routine && ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_ARROWS || (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)) { consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction; if (cv == &cv_chooseskin || cv == &cv_dummystaff || cv == &cv_nextmap || cv == &cv_newgametype) return true; if (currentMenu != &OP_SoundOptionsDef || itemOn > 3) S_StartSound(NULL, sfx_menu1); routine(-1); return true; } // Why _does_ backspace go back anyway? //currentMenu->lastOn = itemOn; //if (currentMenu->prevMenu) // M_SetupNextMenu(currentMenu->prevMenu); return false; default: break; } return true; } // // M_Drawer // Called after the view has been rendered, // but before it has been blitted. // void M_Drawer(void) { if (currentMenu == &MessageDef) menuactive = true; if (menuactive) { // now that's more readable with a faded background (yeah like Quake...) if (!WipeInAction) V_DrawFadeScreen(0xFF00, 16); if (currentMenu->drawroutine) { M_GetGametypeColor(); currentMenu->drawroutine(); // call current menu Draw routine } // Draw version down in corner // ... but only in the MAIN MENU. I'm a picky bastard. if (currentMenu == &MainDef) { if (customversionstring[0] != '\0') { V_DrawThinString(vid.dupx, vid.height - 20*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT, "Mod version:"); V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, customversionstring); } else { #ifdef DEVELOP // Development -- show revision / branch info V_DrawThinString(vid.dupx, vid.height - 20*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, compbranch); V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, comprevision); #else // Regular build V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING)); #endif } } } // focus lost notification goes on top of everything, even the former everything if (window_notinfocus && cv_showfocuslost.value) { M_DrawTextBox((BASEVIDWIDTH/2) - (60), (BASEVIDHEIGHT/2) - (16), 13, 2); if (gamestate == GS_LEVEL && (P_AutoPause() || paused)) V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), highlightflags, "Game Paused"); else V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), highlightflags, "Focus Lost"); } } // // M_StartControlPanel // void M_StartControlPanel(void) { // time attack HACK if (modeattacking && demo.playback) { G_CheckDemoStatus(); S_ChangeMusicInternal("racent", true); return; } // intro might call this repeatedly if (menuactive) { CON_ToggleOff(); // move away console return; } menuactive = true; if (!Playing()) { // Secret menu! //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); currentMenu = &MainDef; itemOn = singleplr; } else if (modeattacking) { currentMenu = &MAPauseDef; itemOn = mapause_continue; } else if (!(netgame || multiplayer)) // Single Player { if (gamestate != GS_LEVEL /*|| ultimatemode*/) // intermission, so gray out stuff. { SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA)) ? (IT_GRAYEDOUT) : (IT_DISABLED); SPauseMenu[spause_retry].status = IT_GRAYEDOUT; } else { //INT32 numlives = 2; SPauseMenu[spause_pandora].status = (M_SecretUnlocked(SECRET_PANDORA)) ? (IT_STRING | IT_CALL) : (IT_DISABLED); /*if (&players[consoleplayer]) { numlives = players[consoleplayer].lives; if (players[consoleplayer].playerstate != PST_LIVE) ++numlives; } // The list of things that can disable retrying is (was?) a little too complex // for me to want to use the short if statement syntax if (numlives <= 1 || G_IsSpecialStage(gamemap)) SPauseMenu[spause_retry].status = (IT_GRAYEDOUT); else*/ SPauseMenu[spause_retry].status = (IT_STRING | IT_CALL); } // We can always use level select though. :33 //SPauseMenu[spause_levelselect].status = (gamecomplete) ? (IT_STRING | IT_CALL) : (IT_DISABLED); // And emblem hints. SPauseMenu[spause_hints].status = (M_SecretUnlocked(SECRET_EMBLEMHINTS)) ? (IT_STRING | IT_CALL) : (IT_DISABLED); // Shift up Pandora's Box if both pandora and levelselect are active /*if (SPauseMenu[spause_pandora].status != (IT_DISABLED) && SPauseMenu[spause_levelselect].status != (IT_DISABLED)) SPauseMenu[spause_pandora].alphaKey = 24; else SPauseMenu[spause_pandora].alphaKey = 32;*/ currentMenu = &SPauseDef; itemOn = spause_continue; } else // multiplayer { MPauseMenu[mpause_switchmap].status = IT_DISABLED; MPauseMenu[mpause_addons].status = IT_DISABLED; MPauseMenu[mpause_scramble].status = IT_DISABLED; MPauseMenu[mpause_psetupsplit].status = IT_DISABLED; MPauseMenu[mpause_psetupsplit2].status = IT_DISABLED; MPauseMenu[mpause_psetupsplit3].status = IT_DISABLED; MPauseMenu[mpause_psetupsplit4].status = IT_DISABLED; MPauseMenu[mpause_spectate].status = IT_DISABLED; MPauseMenu[mpause_entergame].status = IT_DISABLED; MPauseMenu[mpause_canceljoin].status = IT_DISABLED; MPauseMenu[mpause_switchteam].status = IT_DISABLED; MPauseMenu[mpause_switchspectate].status = IT_DISABLED; MPauseMenu[mpause_psetup].status = IT_DISABLED; MISC_ChangeTeamMenu[0].status = IT_DISABLED; MISC_ChangeSpectateMenu[0].status = IT_DISABLED; // Reset these in case splitscreen messes things up MPauseMenu[mpause_switchteam].alphaKey = 48; MPauseMenu[mpause_switchspectate].alphaKey = 48; MPauseMenu[mpause_options].alphaKey = 64; MPauseMenu[mpause_title].alphaKey = 80; MPauseMenu[mpause_quit].alphaKey = 88; Dummymenuplayer_OnChange(); if ((server || IsPlayerAdmin(consoleplayer))) { MPauseMenu[mpause_switchmap].status = IT_STRING | IT_CALL; MPauseMenu[mpause_addons].status = IT_STRING | IT_CALL; if (G_GametypeHasTeams()) MPauseMenu[mpause_scramble].status = IT_STRING | IT_SUBMENU; } if (splitscreen) { MPauseMenu[mpause_psetupsplit].status = MPauseMenu[mpause_psetupsplit2].status = IT_STRING | IT_CALL; MISC_ChangeTeamMenu[0].status = MISC_ChangeSpectateMenu[0].status = IT_STRING|IT_CVAR; if (netgame) { if (G_GametypeHasTeams()) { MPauseMenu[mpause_switchteam].status = IT_STRING | IT_SUBMENU; MPauseMenu[mpause_switchteam].alphaKey += ((splitscreen+1) * 8); MPauseMenu[mpause_options].alphaKey += 8; MPauseMenu[mpause_title].alphaKey += 8; MPauseMenu[mpause_quit].alphaKey += 8; } else if (G_GametypeHasSpectators()) { MPauseMenu[mpause_switchspectate].status = IT_STRING | IT_SUBMENU; MPauseMenu[mpause_switchspectate].alphaKey += ((splitscreen+1) * 8); MPauseMenu[mpause_options].alphaKey += 8; MPauseMenu[mpause_title].alphaKey += 8; MPauseMenu[mpause_quit].alphaKey += 8; } } if (splitscreen > 1) { MPauseMenu[mpause_psetupsplit3].status = IT_STRING | IT_CALL; MPauseMenu[mpause_options].alphaKey += 8; MPauseMenu[mpause_title].alphaKey += 8; MPauseMenu[mpause_quit].alphaKey += 8; if (splitscreen > 2) { MPauseMenu[mpause_psetupsplit4].status = IT_STRING | IT_CALL; MPauseMenu[mpause_options].alphaKey += 8; MPauseMenu[mpause_title].alphaKey += 8; MPauseMenu[mpause_quit].alphaKey += 8; } } } else { MPauseMenu[mpause_psetup].status = IT_STRING | IT_CALL; if (G_GametypeHasTeams()) MPauseMenu[mpause_switchteam].status = IT_STRING | IT_SUBMENU; else if (G_GametypeHasSpectators()) { if (!players[consoleplayer].spectator) MPauseMenu[mpause_spectate].status = IT_STRING | IT_CALL; else if (players[consoleplayer].pflags & PF_WANTSTOJOIN) MPauseMenu[mpause_canceljoin].status = IT_STRING | IT_CALL; else MPauseMenu[mpause_entergame].status = IT_STRING | IT_CALL; } else // in this odd case, we still want something to be on the menu even if it's useless MPauseMenu[mpause_spectate].status = IT_GRAYEDOUT; } currentMenu = &MPauseDef; itemOn = mpause_continue; } CON_ToggleOff(); // move away console } void M_EndModeAttackRun(void) { M_ModeAttackEndGame(0); } // // M_ClearMenus // void M_ClearMenus(boolean callexitmenufunc) { if (!menuactive) return; if (currentMenu->quitroutine && callexitmenufunc && !currentMenu->quitroutine()) return; // we can't quit this menu (also used to set parameter from the menu) #ifndef DC // Save the config file. I'm sick of crashing the game later and losing all my changes! COM_BufAddText(va("saveconfig \"%s\" -silent\n", configfile)); #endif //Alam: But not on the Dreamcast's VMUs if (currentMenu == &MessageDef) // Oh sod off! currentMenu = &MainDef; // Not like it matters menuactive = false; } // // M_SetupNextMenu // void M_SetupNextMenu(menu_t *menudef) { INT16 i; if (currentMenu->quitroutine) { // If you're going from a menu to itself, why are you running the quitroutine? You're not quitting it! -SH if (currentMenu != menudef && !currentMenu->quitroutine()) return; // we can't quit this menu (also used to set parameter from the menu) } currentMenu = menudef; itemOn = currentMenu->lastOn; // in case of... if (itemOn >= currentMenu->numitems) itemOn = currentMenu->numitems - 1; // the curent item can be disabled, // this code go up until an enabled item found if ((currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE) { for (i = 0; i < currentMenu->numitems; i++) { if ((currentMenu->menuitems[i].status & IT_TYPE) != IT_SPACE) { itemOn = i; break; } } } } // // M_Ticker // void M_Ticker(void) { // reset input trigger noFurtherInput = false; if (dedicated) return; if (--skullAnimCounter <= 0) skullAnimCounter = 8; //added : 30-01-98 : test mode for five seconds if (vidm_testingmode > 0) { // restore the previous video mode if (--vidm_testingmode == 0) setmodeneeded = vidm_previousmode + 1; } } // // M_Init // void M_Init(void) { UINT8 i; COM_AddCommand("manual", Command_Manual_f); CV_RegisterVar(&cv_nextmap); CV_RegisterVar(&cv_newgametype); CV_RegisterVar(&cv_chooseskin); CV_RegisterVar(&cv_autorecord); if (dedicated) return; // Menu hacks CV_RegisterVar(&cv_dummymenuplayer); CV_RegisterVar(&cv_dummyteam); CV_RegisterVar(&cv_dummyspectate); CV_RegisterVar(&cv_dummyscramble); CV_RegisterVar(&cv_dummyrings); CV_RegisterVar(&cv_dummylives); CV_RegisterVar(&cv_dummycontinues); //CV_RegisterVar(&cv_dummymares); CV_RegisterVar(&cv_dummystaff); quitmsg[QUITMSG] = M_GetText("Eggman's tied explosives\nto your girlfriend, and\nwill activate them if\nyou press the 'Y' key!\nPress 'N' to save her!\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG1] = M_GetText("What would Tails say if\nhe saw you quitting the game?\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG2] = M_GetText("Hey!\nWhere do ya think you're goin'?\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG3] = M_GetText("Forget your studies!\nPlay some more!\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG4] = M_GetText("You're trying to say you\nlike Sonic R better than\nthis, aren't you?\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG5] = M_GetText("Don't leave yet -- there's a\nsuper emerald around that corner!\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG6] = M_GetText("You'd rather work than play?\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG7] = M_GetText("Go ahead and leave. See if I care...\n*sniffle*\n\n(Press 'Y' to quit)"); quitmsg[QUIT2MSG] = M_GetText("If you leave now,\nEggman will take over the world!\n\n(Press 'Y' to quit)"); quitmsg[QUIT2MSG1] = M_GetText("On your mark,\nget set,\nhit the 'N' key!\n\n(Press 'Y' to quit)"); quitmsg[QUIT2MSG2] = M_GetText("Aw c'mon, just\na few more laps!\n\n(Press 'Y' to quit)"); quitmsg[QUIT2MSG3] = M_GetText("Did you get all those Chaos Emeralds?\n\n(Press 'Y' to quit)"); quitmsg[QUIT2MSG4] = M_GetText("If you leave, I'll use\nmy Jawz on you!\n\n(Press 'Y' to quit)"); quitmsg[QUIT2MSG5] = M_GetText("Don't go!\nYou might find the hidden\nlevels!\n\n(Press 'Y' to quit)"); quitmsg[QUIT2MSG6] = M_GetText("Hit the 'N' key, Sonic!\nThe 'N' key!\n\n(Press 'Y' to quit)"); quitmsg[QUIT3MSG] = M_GetText("Are you really going to give up?\nWe certainly would never give you up.\n\n(Press 'Y' to quit)"); quitmsg[QUIT3MSG1] = M_GetText("Come on, just ONE more netgame!\n\n(Press 'Y' to quit)"); quitmsg[QUIT3MSG2] = M_GetText("Press 'N' to unlock\nthe Golden Kart!\n\n(Press 'Y' to quit)"); quitmsg[QUIT3MSG3] = M_GetText("Couldn't handle\nthe banana meta?\n\n(Press 'Y' to quit)"); quitmsg[QUIT3MSG4] = M_GetText("Every time you press 'Y', an\nSRB2Kart Developer cries...\n\n(Press 'Y' to quit)"); quitmsg[QUIT3MSG5] = M_GetText("You'll be back to play soon, though...\n...right?\n\n(Press 'Y' to quit)"); quitmsg[QUIT3MSG6] = M_GetText("Aww, is Eggman's Nightclub too\ndifficult for you?\n\n(Press 'Y' to quit)"); // Setup PlayerMenu table for (i = 0; i < MAXSKINS; i++) { PlayerMenu[i].status = (i == 0 ? IT_CALL : IT_DISABLED); PlayerMenu[i].patch = PlayerMenu[i].text = NULL; PlayerMenu[i].itemaction = M_ChoosePlayer; PlayerMenu[i].alphaKey = 0; } #ifdef HWRENDER // Permanently hide some options based on render mode if (rendermode == render_soft) OP_VideoOptionsMenu[op_video_ogl].status = OP_VideoOptionsMenu[op_video_md2].status = IT_DISABLED; #endif #ifndef NONET CV_RegisterVar(&cv_serversort); #endif //todo put this somewhere better... CV_RegisterVar(&cv_allcaps); } // ========================================================================== // SPECIAL MENU OPTION DRAW ROUTINES GO HERE // ========================================================================== // Converts a string into question marks. // Used for the secrets menu, to hide yet-to-be-unlocked stuff. static const char *M_CreateSecretMenuOption(const char *str) { static char qbuf[32]; int i; for (i = 0; i < 31; ++i) { if (!str[i]) { qbuf[i] = '\0'; return qbuf; } else if (str[i] != ' ') qbuf[i] = '?'; else qbuf[i] = ' '; } qbuf[31] = '\0'; return qbuf; } static void M_DrawThermo(INT32 x, INT32 y, consvar_t *cv) { INT32 xx = x, i; lumpnum_t leftlump, rightlump, centerlump[2], cursorlump; patch_t *p; leftlump = W_GetNumForName("M_THERML"); rightlump = W_GetNumForName("M_THERMR"); centerlump[0] = W_GetNumForName("M_THERMM"); centerlump[1] = W_GetNumForName("M_THERMM"); cursorlump = W_GetNumForName("M_THERMO"); V_DrawScaledPatch(xx, y, 0, p = W_CachePatchNum(leftlump,PU_CACHE)); xx += SHORT(p->width) - SHORT(p->leftoffset); for (i = 0; i < 16; i++) { V_DrawScaledPatch(xx, y, V_WRAPX, W_CachePatchNum(centerlump[i & 1], PU_CACHE)); xx += 8; } V_DrawScaledPatch(xx, y, 0, W_CachePatchNum(rightlump, PU_CACHE)); xx = (cv->value - cv->PossibleValue[0].value) * (15*8) / (cv->PossibleValue[1].value - cv->PossibleValue[0].value); V_DrawScaledPatch((x + 8) + xx, y, 0, W_CachePatchNum(cursorlump, PU_CACHE)); } // A smaller 'Thermo', with range given as percents (0-100) static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv, boolean ontop) { INT32 i; INT32 range; patch_t *p; for (i = 0; cv->PossibleValue[i+1].strvalue; i++); x = BASEVIDWIDTH - x - SLIDER_WIDTH; if (ontop) { V_DrawCharacter(x - 16 - (skullAnimCounter/5), y, '\x1C' | highlightflags, false); // left arrow V_DrawCharacter(x+(SLIDER_RANGE*8) + 8 + (skullAnimCounter/5), y, '\x1D' | highlightflags, false); // right arrow } if ((range = atoi(cv->defaultvalue)) != cv->value) { range = ((range - cv->PossibleValue[0].value) * 100 / (cv->PossibleValue[1].value - cv->PossibleValue[0].value)); if (range < 0) range = 0; if (range > 100) range = 100; // draw the default p = W_CachePatchName("M_SLIDEC", PU_CACHE); V_DrawScaledPatch(x - 4 + (((SLIDER_RANGE)*8 + 4)*range)/100, y, 0, p); } V_DrawScaledPatch(x - 8, y, 0, W_CachePatchName("M_SLIDEL", PU_CACHE)); p = W_CachePatchName("M_SLIDEM", PU_CACHE); for (i = 0; i < SLIDER_RANGE; i++) V_DrawScaledPatch (x+i*8, y, 0,p); p = W_CachePatchName("M_SLIDER", PU_CACHE); V_DrawScaledPatch(x+SLIDER_RANGE*8, y, 0, p); range = ((cv->value - cv->PossibleValue[0].value) * 100 / (cv->PossibleValue[1].value - cv->PossibleValue[0].value)); if (range < 0) range = 0; if (range > 100) range = 100; // draw the slider cursor p = W_CachePatchName("M_SLIDEC", PU_CACHE); V_DrawScaledPatch(x - 4 + (((SLIDER_RANGE)*8 + 4)*range)/100, y, 0, p); } // // Draw a textbox, like Quake does, because sometimes it's difficult // to read the text with all the stuff in the background... // void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines) { // Solid color textbox. V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 239); //V_DrawFill(x+8, y+8, width*8, boxlines*8, 31); /* patch_t *p; INT32 cx, cy, n; INT32 step, boff; step = 8; boff = 8; // draw left side cx = x; cy = y; V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TL], PU_CACHE)); cy += boff; p = W_CachePatchNum(viewborderlump[BRDR_L], PU_CACHE); for (n = 0; n < boxlines; n++) { V_DrawScaledPatch(cx, cy, V_WRAPY, p); cy += step; } V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BL], PU_CACHE)); // draw middle V_DrawFlatFill(x + boff, y + boff, width*step, boxlines*step, st_borderpatchnum); cx += boff; cy = y; while (width > 0) { V_DrawScaledPatch(cx, cy, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_T], PU_CACHE)); V_DrawScaledPatch(cx, y + boff + boxlines*step, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_B], PU_CACHE)); width--; cx += step; } // draw right side cy = y; V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_TR], PU_CACHE)); cy += boff; p = W_CachePatchNum(viewborderlump[BRDR_R], PU_CACHE); for (n = 0; n < boxlines; n++) { V_DrawScaledPatch(cx, cy, V_WRAPY, p); cy += step; } V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BR], PU_CACHE)); */ } // // Draw border for the savegame description // /*static void M_DrawSaveLoadBorder(INT32 x,INT32 y) { INT32 i; V_DrawScaledPatch (x-8,y+7,0,W_CachePatchName("M_LSLEFT",PU_CACHE)); for (i = 0;i < 24;i++) { V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSCNTR",PU_CACHE)); x += 8; } V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSRGHT",PU_CACHE)); }*/ // horizontally centered text static void M_CentreText(INT32 y, const char *string) { INT32 x; //added : 02-02-98 : centre on 320, because V_DrawString centers on vid.width... x = (BASEVIDWIDTH - V_StringWidth(string, V_OLDSPACING))>>1; V_DrawString(x,y,V_OLDSPACING,string); } // // M_DrawMapEmblems // // used by pause & statistics to draw a row of emblems for a map // static void M_DrawMapEmblems(INT32 mapnum, INT32 x, INT32 y) { UINT8 lasttype = UINT8_MAX, curtype; emblem_t *emblem = M_GetLevelEmblems(mapnum); while (emblem) { switch (emblem->type) { case ET_TIME: //case ET_SCORE: case ET_RINGS: curtype = 1; break; /*case ET_NGRADE: case ET_NTIME: curtype = 2; break;*/ default: curtype = 0; break; } // Shift over if emblem is of a different discipline if (lasttype != UINT8_MAX && lasttype != curtype) x -= 4; lasttype = curtype; if (emblem->collected) V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE)); else V_DrawSmallScaledPatch(x, y, 0, W_CachePatchName("NEEDIT", PU_CACHE)); emblem = M_GetLevelEmblems(-1); x -= 8; } } static void M_DrawMenuTitle(void) { if (currentMenu->menutitlepic) { patch_t *p = W_CachePatchName(currentMenu->menutitlepic, PU_CACHE); if (p->height > 24) // title is larger than normal { INT32 xtitle = (BASEVIDWIDTH - (SHORT(p->width)/2))/2; INT32 ytitle = (30 - (SHORT(p->height)/2))/2; if (xtitle < 0) xtitle = 0; if (ytitle < 0) ytitle = 0; V_DrawSmallScaledPatch(xtitle, ytitle, 0, p); } else { INT32 xtitle = (BASEVIDWIDTH - SHORT(p->width))/2; INT32 ytitle = (30 - SHORT(p->height))/2; if (xtitle < 0) xtitle = 0; if (ytitle < 0) ytitle = 0; V_DrawScaledPatch(xtitle, ytitle, 0, p); } } } static void M_DrawGenericMenu(void) { INT32 x, y, w, i, cursory = 0; // DRAW MENU x = currentMenu->x; y = currentMenu->y; // draw title (or big pic) M_DrawMenuTitle(); for (i = 0; i < currentMenu->numitems; i++) { if (i == itemOn) cursory = y; switch (currentMenu->menuitems[i].status & IT_DISPLAY) { case IT_PATCH: if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0]) { if (currentMenu->menuitems[i].status & IT_CENTER) { patch_t *p; p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, y, 0, p); } else { V_DrawScaledPatch(x, y, 0, W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE)); } } /* FALLTHRU */ case IT_NOTHING: case IT_DYBIGSPACE: y = currentMenu->y+currentMenu->menuitems[i].alphaKey;//+= LINEHEIGHT; break; case IT_BIGSLIDER: M_DrawThermo(x, y, (consvar_t *)currentMenu->menuitems[i].itemaction); y += LINEHEIGHT; break; case IT_STRING: case IT_WHITESTRING: if (currentMenu->menuitems[i].alphaKey) y = currentMenu->y+currentMenu->menuitems[i].alphaKey; if (i == itemOn) cursory = y; if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) V_DrawString(x, y, 0, currentMenu->menuitems[i].text); else V_DrawString(x, y, highlightflags, currentMenu->menuitems[i].text); // Cvar specific handling switch (currentMenu->menuitems[i].status & IT_TYPE) case IT_CVAR: { consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; char asterisks[MAXSTRINGLENGTH+1]; size_t sl; switch (currentMenu->menuitems[i].status & IT_CVARTYPE) { case IT_CV_SLIDER: M_DrawSlider(x, y, cv, (i == itemOn)); case IT_CV_NOPRINT: // color use this case IT_CV_INVISSLIDER: // monitor toggles use this break; case IT_CV_PASSWORD: if (i == itemOn) { V_DrawRightAlignedThinString(x + MAXSTRINGLENGTH*8 + 10, y, V_ALLOWLOWERCASE, va(M_GetText("Tab: %s password"), cv->value ? "hide" : "show")); } if (!cv->value || i != itemOn) { sl = strlen(cv->string); memset(asterisks, '*', sl); memset(asterisks + sl, 0, MAXSTRINGLENGTH+1-sl); M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1); V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, asterisks); if (skullAnimCounter < 4 && i == itemOn) V_DrawCharacter(x + 8 + V_StringWidth(asterisks, 0), y + 12, '_' | 0x80, false); y += 16; break; } /* fallthru */ case IT_CV_STRING: M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1); V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string); if (skullAnimCounter < 4 && i == itemOn) V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12, '_' | 0x80, false); y += 16; break; default: w = V_StringWidth(cv->string, 0); V_DrawString(BASEVIDWIDTH - x - w, y, ((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string); if (i == itemOn) { V_DrawCharacter(BASEVIDWIDTH - x - 10 - w - (skullAnimCounter/5), y, '\x1C' | highlightflags, false); // left arrow V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y, '\x1D' | highlightflags, false); // right arrow } break; } break; } y += STRINGHEIGHT; break; case IT_STRING2: V_DrawString(x, y, 0, currentMenu->menuitems[i].text); /* FALLTHRU */ case IT_DYLITLSPACE: y += SMALLLINEHEIGHT; break; case IT_GRAYPATCH: if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0]) V_DrawMappedPatch(x, y, 0, W_CachePatchName(currentMenu->menuitems[i].patch,PU_CACHE), graymap); y += LINEHEIGHT; break; case IT_TRANSTEXT: if (currentMenu->menuitems[i].alphaKey) y = currentMenu->y+currentMenu->menuitems[i].alphaKey; /* FALLTHRU */ case IT_TRANSTEXT2: V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text); y += SMALLLINEHEIGHT; break; case IT_QUESTIONMARKS: if (currentMenu->menuitems[i].alphaKey) y = currentMenu->y+currentMenu->menuitems[i].alphaKey; V_DrawString(x, y, V_TRANSLUCENT|V_OLDSPACING, M_CreateSecretMenuOption(currentMenu->menuitems[i].text)); y += SMALLLINEHEIGHT; break; case IT_HEADERTEXT: // draws 16 pixels to the left, in yellow text if (currentMenu->menuitems[i].alphaKey) y = currentMenu->y+currentMenu->menuitems[i].alphaKey; V_DrawString(x-16, y, highlightflags, currentMenu->menuitems[i].text); y += SMALLLINEHEIGHT; break; } } // DRAW THE SKULL CURSOR if (((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_PATCH) || ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_NOTHING)) { V_DrawScaledPatch(currentMenu->x + SKULLXOFF, cursory - 5, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); } else { V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); V_DrawString(currentMenu->x, cursory, highlightflags, currentMenu->menuitems[itemOn].text); } } static void M_DrawGenericBackgroundMenu(void) { V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); M_DrawGenericMenu(); } static void M_DrawPauseMenu(void) { #if 0 if (!netgame && !multiplayer && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) { emblem_t *emblem_detail[3] = {NULL, NULL, NULL}; char emblem_text[3][20]; INT32 i; M_DrawTextBox(27, 16, 32, 6); // Draw any and all emblems at the top. M_DrawMapEmblems(gamemap, 272, 28); if (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0) { if (strlen(mapheaderinfo[gamemap-1]->actnum) > 0) V_DrawString(40, 28, highlightflags, va("%s %s %s", mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl, mapheaderinfo[gamemap-1]->actnum)); else V_DrawString(40, 28, highlightflags, va("%s %s", mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl)); } else { if (strlen(mapheaderinfo[gamemap-1]->actnum) > 0) V_DrawString(40, 28, highlightflags, va("%s %s", mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->actnum)); else V_DrawString(40, 28, highlightflags, mapheaderinfo[gamemap-1]->lvlttl); } // Set up the detail boxes. { emblem_t *emblem = M_GetLevelEmblems(gamemap); while (emblem) { INT32 emblemslot; char targettext[9], currenttext[9]; switch (emblem->type) { /*case ET_SCORE: snprintf(targettext, 9, "%d", emblem->var); snprintf(currenttext, 9, "%u", G_GetBestScore(gamemap)); targettext[8] = 0; currenttext[8] = 0; emblemslot = 0; break;*/ case ET_TIME: emblemslot = emblem->var; // dumb hack snprintf(targettext, 9, "%i:%02i.%02i", G_TicsToMinutes((tic_t)emblemslot, false), G_TicsToSeconds((tic_t)emblemslot), G_TicsToCentiseconds((tic_t)emblemslot)); emblemslot = (INT32)G_GetBestTime(gamemap); // dumb hack pt ii if ((tic_t)emblemslot == UINT32_MAX) snprintf(currenttext, 9, "-:--.--"); else snprintf(currenttext, 9, "%i:%02i.%02i", G_TicsToMinutes((tic_t)emblemslot, false), G_TicsToSeconds((tic_t)emblemslot), G_TicsToCentiseconds((tic_t)emblemslot)); targettext[8] = 0; currenttext[8] = 0; emblemslot = 1; break; /*case ET_RINGS: snprintf(targettext, 9, "%d", emblem->var); snprintf(currenttext, 9, "%u", G_GetBestRings(gamemap)); targettext[8] = 0; currenttext[8] = 0; emblemslot = 2; break; case ET_NGRADE: snprintf(targettext, 9, "%u", P_GetScoreForGrade(gamemap, 0, emblem->var)); snprintf(currenttext, 9, "%u", G_GetBestNightsScore(gamemap, 0)); targettext[8] = 0; currenttext[8] = 0; emblemslot = 1; break; case ET_NTIME: emblemslot = emblem->var; // dumb hack pt iii snprintf(targettext, 9, "%i:%02i.%02i", G_TicsToMinutes((tic_t)emblemslot, false), G_TicsToSeconds((tic_t)emblemslot), G_TicsToCentiseconds((tic_t)emblemslot)); emblemslot = (INT32)G_GetBestNightsTime(gamemap, 0); // dumb hack pt iv if ((tic_t)emblemslot == UINT32_MAX) snprintf(currenttext, 9, "-:--.--"); else snprintf(currenttext, 9, "%i:%02i.%02i", G_TicsToMinutes((tic_t)emblemslot, false), G_TicsToSeconds((tic_t)emblemslot), G_TicsToCentiseconds((tic_t)emblemslot)); targettext[8] = 0; currenttext[8] = 0; emblemslot = 2; break;*/ default: goto bademblem; } if (emblem_detail[emblemslot]) goto bademblem; emblem_detail[emblemslot] = emblem; snprintf(emblem_text[emblemslot], 20, "%8s /%8s", currenttext, targettext); emblem_text[emblemslot][19] = 0; bademblem: emblem = M_GetLevelEmblems(-1); } } for (i = 0; i < 3; ++i) { emblem_t *emblem = emblem_detail[i]; if (!emblem) continue; if (emblem->collected) V_DrawSmallMappedPatch(40, 44 + (i*8), 0, W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE)); else V_DrawSmallScaledPatch(40, 44 + (i*8), 0, W_CachePatchName("NEEDIT", PU_CACHE)); switch (emblem->type) { /*case ET_SCORE: case ET_NGRADE: V_DrawString(56, 44 + (i*8), highlightflags, "SCORE:"); break;*/ case ET_TIME: //case ET_NTIME: V_DrawString(56, 44 + (i*8), highlightflags, "TIME:"); break; /*case ET_RINGS: V_DrawString(56, 44 + (i*8), highlightflags, "RINGS:"); break;*/ } V_DrawRightAlignedString(284, 44 + (i*8), V_MONOSPACE, emblem_text[i]); } } #endif M_DrawGenericMenu(); } static void M_DrawCenteredMenu(void) { INT32 x, y, i, cursory = 0; // DRAW MENU x = currentMenu->x; y = currentMenu->y; // draw title (or big pic) M_DrawMenuTitle(); for (i = 0; i < currentMenu->numitems; i++) { if (i == itemOn) cursory = y; switch (currentMenu->menuitems[i].status & IT_DISPLAY) { case IT_PATCH: if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0]) { if (currentMenu->menuitems[i].status & IT_CENTER) { patch_t *p; p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, y, 0, p); } else { V_DrawScaledPatch(x, y, 0, W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE)); } } /* FALLTHRU */ case IT_NOTHING: case IT_DYBIGSPACE: y += LINEHEIGHT; break; case IT_BIGSLIDER: M_DrawThermo(x, y, (consvar_t *)currentMenu->menuitems[i].itemaction); y += LINEHEIGHT; break; case IT_STRING: case IT_WHITESTRING: if (currentMenu->menuitems[i].alphaKey) y = currentMenu->y+currentMenu->menuitems[i].alphaKey; if (i == itemOn) cursory = y; if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) V_DrawCenteredString(x, y, 0, currentMenu->menuitems[i].text); else V_DrawCenteredString(x, y, highlightflags, currentMenu->menuitems[i].text); // Cvar specific handling switch(currentMenu->menuitems[i].status & IT_TYPE) case IT_CVAR: { consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; switch(currentMenu->menuitems[i].status & IT_CVARTYPE) { case IT_CV_SLIDER: M_DrawSlider(x, y, cv, (i == itemOn)); case IT_CV_NOPRINT: // color use this break; case IT_CV_STRING: M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1); V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string); if (skullAnimCounter < 4 && i == itemOn) V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12, '_' | 0x80, false); y += 16; break; default: V_DrawString(BASEVIDWIDTH - x - V_StringWidth(cv->string, 0), y, ((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string); break; } break; } y += STRINGHEIGHT; break; case IT_STRING2: V_DrawCenteredString(x, y, 0, currentMenu->menuitems[i].text); /* FALLTHRU */ case IT_DYLITLSPACE: y += SMALLLINEHEIGHT; break; case IT_QUESTIONMARKS: if (currentMenu->menuitems[i].alphaKey) y = currentMenu->y+currentMenu->menuitems[i].alphaKey; V_DrawCenteredString(x, y, V_TRANSLUCENT|V_OLDSPACING, M_CreateSecretMenuOption(currentMenu->menuitems[i].text)); y += SMALLLINEHEIGHT; break; case IT_GRAYPATCH: if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0]) V_DrawMappedPatch(x, y, 0, W_CachePatchName(currentMenu->menuitems[i].patch,PU_CACHE), graymap); y += LINEHEIGHT; break; } } // DRAW THE SKULL CURSOR if (((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_PATCH) || ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_NOTHING)) { V_DrawScaledPatch(x + SKULLXOFF, cursory - 5, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); } else { V_DrawScaledPatch(x - V_StringWidth(currentMenu->menuitems[itemOn].text, 0)/2 - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); V_DrawCenteredString(x, cursory, highlightflags, currentMenu->menuitems[itemOn].text); } } // // M_StringHeight // // Find string height from hu_font chars // static inline size_t M_StringHeight(const char *string) { size_t h = 8, i; for (i = 0; i < strlen(string); i++) if (string[i] == '\n') h += 8; return h; } // ========================================================================== // Extraneous menu patching functions // ========================================================================== // // M_PatchSkinNameTable // // Like M_PatchLevelNameTable, but for cv_chooseskin // static void M_PatchSkinNameTable(void) { INT32 j; memset(skins_cons_t, 0, sizeof (skins_cons_t)); for (j = 0; j < MAXSKINS; j++) { if (skins[j].name[0] != '\0') { skins_cons_t[j].strvalue = skins[j].name; skins_cons_t[j].value = j+1; } else { skins_cons_t[j].strvalue = NULL; skins_cons_t[j].value = 0; break; } } j = R_SkinAvailable(cv_skin.string); if (j == -1) j = 0; CV_SetValue(&cv_chooseskin, j+1); // This causes crash sometimes?! return; } // Call before showing any level-select menus static void M_PrepareLevelSelect(void) { if (levellistmode != LLM_CREATESERVER) CV_SetValue(&cv_nextmap, M_GetFirstLevelInList()); else Newgametype_OnChange(); // Make sure to start on an appropriate map if wads have been added } // // M_CanShowLevelInList // // Determines whether to show a given map in the various level-select lists. // Set gt = -1 to ignore gametype. // boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) { // Random map! if (mapnum == -1) return (gamestate != GS_TIMEATTACK && !modeattacking); // Does the map exist? if (!mapheaderinfo[mapnum]) return false; // Does the map have a name? if (!mapheaderinfo[mapnum]->lvlttl[0]) return false; switch (levellistmode) { case LLM_CREATESERVER: // Should the map be hidden? if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU && mapnum+1 != gamemap) return false; if (M_MapLocked(mapnum+1)) return false; // not unlocked /*if (gt == GT_COOP && (mapheaderinfo[mapnum]->typeoflevel & TOL_COOP)) return true; if (gt == GT_COMPETITION && (mapheaderinfo[mapnum]->typeoflevel & TOL_COMPETITION)) return true; if (gt == GT_CTF && (mapheaderinfo[mapnum]->typeoflevel & TOL_CTF)) return true; if ((gt == GT_TAG || gt == GT_HIDEANDSEEK) && (mapheaderinfo[mapnum]->typeoflevel & TOL_TAG)) return true;*/ if ((gt == GT_MATCH || gt == GT_TEAMMATCH) && (mapheaderinfo[mapnum]->typeoflevel & TOL_MATCH)) return true; if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) return true; return false; /*case LLM_LEVELSELECT: if (mapheaderinfo[mapnum]->levelselect != maplistoption) return false; if (M_MapLocked(mapnum+1)) return false; // not unlocked return true;*/ case LLM_RECORDATTACK: /*if (!(mapheaderinfo[mapnum]->menuflags & LF2_RECORDATTACK)) return false;*/ if (!(mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) return false; if (M_MapLocked(mapnum+1)) return false; // not unlocked if (M_SecretUnlocked(SECRET_HELLATTACK)) return true; // now you're in hell if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) return false; // map hell /*if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) return true; if (!mapvisited[mapnum]) return false;*/ return true; /*case LLM_NIGHTSATTACK: if (!(mapheaderinfo[mapnum]->menuflags & LF2_NIGHTSATTACK)) return false; if (M_MapLocked(mapnum+1)) return false; // not unlocked if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) return true; if (!mapvisited[mapnum]) return false; return true;*/ default: return false; } // Hmm? Couldn't decide? return false; } static INT32 M_CountLevelsToShowInList(void) { INT32 mapnum, count = 0; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) if (M_CanShowLevelInList(mapnum, -1)) count++; return count; } static INT32 M_GetFirstLevelInList(void) { INT32 mapnum; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) if (M_CanShowLevelInList(mapnum, -1)) return mapnum + 1; return 1; } // ================================================== // MESSAGE BOX (aka: a hacked, cobbled together menu) // ================================================== static void M_DrawMessageMenu(void); // Because this is just a hack-ish 'menu', I'm not putting this with the others static menuitem_t MessageMenu[] = { // TO HACK {0,NULL, NULL, NULL,0} }; menu_t MessageDef = { NULL, // title 1, // # of menu items NULL, // previous menu (TO HACK) MessageMenu, // menuitem_t -> M_DrawMessageMenu, // drawing routine -> 0, 0, // x, y (TO HACK) 0, // lastOn, flags (TO HACK) NULL }; void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype) { size_t max = 0, start = 0, i, strlines; static char *message = NULL; Z_Free(message); message = Z_StrDup(string); DEBFILE(message); // Rudementary word wrapping. // Simple and effective. Does not handle nonuniform letter sizes, colors, etc. but who cares. strlines = 0; for (i = 0; message[i]; i++) { if (message[i] == ' ') { start = i; max += 4; } else if (message[i] == '\n') { strlines = i; start = 0; max = 0; continue; } else max += 8; // Start trying to wrap if presumed length exceeds the screen width. if (max >= BASEVIDWIDTH && start > 0) { message[start] = '\n'; max -= (start-strlines)*8; strlines = start; start = 0; } } start = 0; max = 0; M_StartControlPanel(); // can't put menuactive to true if (currentMenu == &MessageDef) // Prevent recursion MessageDef.prevMenu = &MainDef; else MessageDef.prevMenu = currentMenu; MessageDef.menuitems[0].text = message; MessageDef.menuitems[0].alphaKey = (UINT8)itemtype; if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING; switch (itemtype) { case MM_NOTHING: MessageDef.menuitems[0].status = IT_MSGHANDLER; MessageDef.menuitems[0].itemaction = M_StopMessage; break; case MM_YESNO: MessageDef.menuitems[0].status = IT_MSGHANDLER; MessageDef.menuitems[0].itemaction = routine; break; case MM_EVENTHANDLER: MessageDef.menuitems[0].status = IT_MSGHANDLER; MessageDef.menuitems[0].itemaction = routine; break; } //added : 06-02-98: now draw a textbox around the message // compute lenght max and the numbers of lines for (strlines = 0; *(message+start); strlines++) { for (i = 0;i < strlen(message+start);i++) { if (*(message+start+i) == '\n') { if (i > max) max = i; start += i; i = (size_t)-1; //added : 07-02-98 : damned! start++; break; } } if (i == strlen(message+start)) start += i; } MessageDef.x = (INT16)((BASEVIDWIDTH - 8*max-16)/2); MessageDef.y = (INT16)((BASEVIDHEIGHT - M_StringHeight(message))/2); MessageDef.lastOn = (INT16)((strlines<<8)+max); //M_SetupNextMenu(); currentMenu = &MessageDef; itemOn = 0; } #define MAXMSGLINELEN 256 static void M_DrawMessageMenu(void) { INT32 y = currentMenu->y; size_t i, start = 0; INT16 max; char string[MAXMSGLINELEN]; INT32 mlines; const char *msg = currentMenu->menuitems[0].text; mlines = currentMenu->lastOn>>8; max = (INT16)((UINT8)(currentMenu->lastOn & 0xFF)*8); // hack: draw RA background in RA menus if (gamestate == GS_TIMEATTACK) V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); M_DrawTextBox(currentMenu->x, y - 8, (max+7)>>3, mlines); while (*(msg+start)) { size_t len = strlen(msg+start); for (i = 0; i < len; i++) { if (*(msg+start+i) == '\n') { memset(string, 0, MAXMSGLINELEN); if (i >= MAXMSGLINELEN) { CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg); return; } else { strncpy(string,msg+start, i); string[i] = '\0'; start += i; i = (size_t)-1; //added : 07-02-98 : damned! start++; } break; } } if (i == strlen(msg+start)) { if (i >= MAXMSGLINELEN) { CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg); return; } else { strcpy(string, msg + start); start += i; } } V_DrawString((BASEVIDWIDTH - V_StringWidth(string, 0))/2,y,V_ALLOWLOWERCASE,string); y += 8; //SHORT(hu_font[0]->height); } } // default message handler static void M_StopMessage(INT32 choice) { (void)choice; if (menuactive) M_SetupNextMenu(MessageDef.prevMenu); } // ========= // IMAGEDEFS // ========= // Draw an Image Def. Aka, Help images. // Defines what image is used in (menuitem_t)->text. // You can even put multiple images in one menu! static void M_DrawImageDef(void) { // this is probably what the V_DrawFixedPatch screen-fill bullshit was for, right //V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); -- never mind, screen fade // Grr. Need to autodetect for pic_ts. pic_t *pictest = (pic_t *)W_CachePatchName(currentMenu->menuitems[itemOn].text,PU_CACHE); if (!pictest->zero) V_DrawScaledPic(0,0,0,W_GetNumForName(currentMenu->menuitems[itemOn].text)); else { patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text,PU_CACHE); if (patch->height <= BASEVIDHEIGHT) V_DrawScaledPatch(0,0,0,patch); else V_DrawSmallScaledPatch(0,0,0,patch); } if (currentMenu->menuitems[itemOn].alphaKey) { V_DrawString(2,BASEVIDHEIGHT-10, V_YELLOWMAP, va("%d", (itemOn<<1)-1)); // intentionally not highlightflags, unlike below V_DrawRightAlignedString(BASEVIDWIDTH-2,BASEVIDHEIGHT-10, V_YELLOWMAP, va("%d", itemOn<<1)); // ditto } else { INT32 x = BASEVIDWIDTH>>1, y = (BASEVIDHEIGHT>>1) - 4; x += (itemOn ? 1 : -1)*((BASEVIDWIDTH>>2) + 10); V_DrawCenteredString(x, y-10, highlightflags, "USE ARROW KEYS"); V_DrawCharacter(x - 10 - (skullAnimCounter/5), y, '\x1C' | highlightflags, false); // left arrow V_DrawCharacter(x + 2 + (skullAnimCounter/5), y, '\x1D' | highlightflags, false); // right arrow V_DrawCenteredString(x, y+10, highlightflags, "TO LEAF THROUGH"); } } // Handles the ImageDefs. Just a specialized function that // uses left and right movement. static void M_HandleImageDef(INT32 choice) { boolean exitmenu = false; switch (choice) { case KEY_RIGHTARROW: if (itemOn >= (INT16)(currentMenu->numitems-1)) break; S_StartSound(NULL, sfx_menu1); itemOn++; break; case KEY_LEFTARROW: if (!itemOn) break; S_StartSound(NULL, sfx_menu1); itemOn--; break; case KEY_ESCAPE: case KEY_ENTER: exitmenu = true; break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } // ====================== // MISC MAIN MENU OPTIONS // ====================== static void M_AddonsOptions(INT32 choice) { (void)choice; Addons_option_Onchange(); M_SetupNextMenu(&OP_AddonsOptionsDef); } #define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make add-ons!" #define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make add-ons!" static void M_Addons(INT32 choice) { const char *pathname = "."; (void)choice; #if 1 if (cv_addons_option.value == 0) pathname = usehome ? srb2home : srb2path; else if (cv_addons_option.value == 1) pathname = srb2home; else if (cv_addons_option.value == 2) pathname = srb2path; else #endif if (cv_addons_option.value == 3 && *cv_addons_folder.string != '\0') pathname = cv_addons_folder.string; strlcpy(menupath, pathname, 1024); menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath) + 1; if (menupath[menupathindex[menudepthleft]-2] != PATHSEP[0]) { menupath[menupathindex[menudepthleft]-1] = PATHSEP[0]; menupath[menupathindex[menudepthleft]] = 0; } else --menupathindex[menudepthleft]; if (!preparefilemenu(false, false)) { M_StartMessage(va("No files/folders found.\n\n%s\n\n(Press a key)\n", (recommendedflags == V_SKYMAP ? LOCATIONSTRING2 : LOCATIONSTRING1)),NULL,MM_NOTHING); return; } else dir_on[menudepthleft] = 0; if (addonsp[0]) // never going to have some provided but not all, saves individually checking { size_t i; for (i = 0; i < NUM_EXT+5; i++) W_UnlockCachedPatch(addonsp[i]); } addonsp[EXT_FOLDER] = W_CachePatchName("M_FFLDR", PU_STATIC); addonsp[EXT_UP] = W_CachePatchName("M_FBACK", PU_STATIC); addonsp[EXT_NORESULTS] = W_CachePatchName("M_FNOPE", PU_STATIC); addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_STATIC); addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_STATIC); addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_STATIC); #ifdef USE_KART addonsp[EXT_KART] = W_CachePatchName("M_FKART", PU_STATIC); #endif addonsp[EXT_PK3] = W_CachePatchName("M_FPK3", PU_STATIC); addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_STATIC); addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_STATIC); addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_STATIC); addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL", PU_STATIC); addonsp[NUM_EXT+2] = W_CachePatchName("M_FLOAD", PU_STATIC); addonsp[NUM_EXT+3] = W_CachePatchName("M_FSRCH", PU_STATIC); addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_STATIC); MISC_AddonsDef.prevMenu = currentMenu; M_SetupNextMenu(&MISC_AddonsDef); } #define width 4 #define vpadding 27 #define h (BASEVIDHEIGHT-(2*vpadding)) #define NUMCOLOURS 8 // when toast's coding it's british english hacker fucker static void M_DrawTemperature(INT32 x, fixed_t t) { INT32 y; // bounds check if (t > FRACUNIT) t = FRACUNIT; /*else if (t < 0) -- not needed t = 0;*/ // scale if (t > 1) t = (FixedMul(h<>FRACBITS); // border V_DrawFill(x - 1, vpadding, 1, h, 120); V_DrawFill(x + width, vpadding, 1, h, 120); V_DrawFill(x - 1, vpadding-1, width+2, 1, 120); V_DrawFill(x - 1, vpadding+h, width+2, 1, 120); // bar itself y = h; if (t) for (t = h - t; y > 0; y--) { UINT8 colours[NUMCOLOURS] = {135, 133, 92, 77, 114, 178, 161, 162}; UINT8 c; if (y <= t) break; if (y+vpadding >= BASEVIDHEIGHT/2) c = 185; else c = colours[(NUMCOLOURS*(y-1))/(h/2)]; V_DrawFill(x, y-1 + vpadding, width, 1, c); } // fill the rest of the backing if (y) V_DrawFill(x, vpadding, width, y, 30); } #undef width #undef vpadding #undef h #undef NUMCOLOURS static char *M_AddonsHeaderPath(void) { UINT32 len; static char header[1024]; strlcpy(header, va("%s folder%s", cv_addons_option.string, menupath+menupathindex[menudepth-1]-1), 1024); len = strlen(header); if (len > 34) { len = len-34; header[len] = header[len+1] = header[len+2] = '.'; } else len = 0; return header+len; } #define UNEXIST S_StartSound(NULL, sfx_s26d);\ M_SetupNextMenu(MISC_AddonsDef.prevMenu);\ M_StartMessage(va("\x82%s\x80\nThis folder no longer exists!\nAborting to main menu.\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING) #define CLEARNAME Z_Free(refreshdirname);\ refreshdirname = NULL static boolean prevmajormods = false; static void M_AddonsClearName(INT32 choice) { if (!majormods || prevmajormods) { CLEARNAME; } M_StopMessage(choice); } // returns whether to do message draw static boolean M_AddonsRefresh(void) { if ((refreshdirmenu & REFRESHDIR_NORMAL) && !preparefilemenu(true, false)) { UNEXIST; if (refreshdirname) { CLEARNAME; } return true; } if (!majormods && prevmajormods) prevmajormods = false; if ((refreshdirmenu & REFRESHDIR_ADDFILE) || (majormods && !prevmajormods)) { char *message = NULL; if (refreshdirmenu & REFRESHDIR_NOTLOADED) { S_StartSound(NULL, sfx_s26d); if (refreshdirmenu & REFRESHDIR_MAX) message = va("%c%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nIf you wish to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); else message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); } else if (refreshdirmenu & (REFRESHDIR_WARNING|REFRESHDIR_ERROR)) { S_StartSound(NULL, sfx_s224); message = va("%c%s\x80\nA file was loaded with %s.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings")); } else if (majormods && !prevmajormods) { S_StartSound(NULL, sfx_s221); message = va("%c%s\x80\nGameplay has now been modified.\nIf you wish to play Record Attack mode, restart the game to clear existing add-ons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); prevmajormods = majormods; } if (message) { M_StartMessage(message,M_AddonsClearName,MM_EVENTHANDLER); return true; } S_StartSound(NULL, sfx_s221); CLEARNAME; } return false; } static void M_DrawAddons(void) { INT32 x, y; ssize_t i, m; const UINT8 *flashcol = NULL; UINT8 hilicol; // hack - need to refresh at end of frame to handle addfile... if (refreshdirmenu & M_AddonsRefresh()) { M_DrawMessageMenu(); return; } if (Playing()) V_DrawCenteredString(BASEVIDWIDTH/2, 5, warningflags, "Adding files mid-game may cause problems."); else V_DrawCenteredString(BASEVIDWIDTH/2, 5, 0, (recommendedflags == V_SKYMAP ? LOCATIONSTRING2 : LOCATIONSTRING1)); if (numwadfiles <= mainwads+1) y = 0; else if (numwadfiles >= MAX_WADFILES) y = FRACUNIT; else { x = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< y) y = x; if (y > FRACUNIT) // happens because of how we're shrinkin' it a little y = FRACUNIT; } M_DrawTemperature(BASEVIDWIDTH - 19 - 5, y); // DRAW MENU x = currentMenu->x; y = currentMenu->y + 1; hilicol = V_GetStringColormap(highlightflags)[120]; V_DrawString(x-21, (y - 16) + (lsheadingheight - 12), highlightflags|V_ALLOWLOWERCASE, M_AddonsHeaderPath()); V_DrawFill(x-21, (y - 16) + (lsheadingheight - 3), MAXSTRINGLENGTH*8+6, 1, hilicol); V_DrawFill(x-21, (y - 16) + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 30); m = (BASEVIDHEIGHT - currentMenu->y + 2) - (y - 1); V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 239); // scrollbar! if (sizedirmenu <= (2*numaddonsshown + 1)) i = 0; else { ssize_t q = m; m = ((2*numaddonsshown + 1) * m)/sizedirmenu; if (dir_on[menudepthleft] <= numaddonsshown) // all the way up i = 0; else if (sizedirmenu <= (dir_on[menudepthleft] + numaddonsshown + 1)) // all the way down i = q-m; else i = ((dir_on[menudepthleft] - numaddonsshown) * (q-m))/(sizedirmenu - (2*numaddonsshown + 1)); } V_DrawFill(x + MAXSTRINGLENGTH*8+5 - 21, (y - 1) + i, 1, m, hilicol); // get bottom... m = dir_on[menudepthleft] + numaddonsshown + 1; if (m > (ssize_t)sizedirmenu) m = sizedirmenu; // then compute top and adjust bottom if needed! if (m < (2*numaddonsshown + 1)) { m = min(sizedirmenu, 2*numaddonsshown + 1); i = 0; } else i = m - (2*numaddonsshown + 1); if (i != 0) V_DrawString(19, y+4 - (skullAnimCounter/5), highlightflags, "\x1A"); if (skullAnimCounter < 4) flashcol = V_GetStringColormap(highlightflags); for (; i < m; i++) { UINT32 flags = V_ALLOWLOWERCASE; if (y > BASEVIDHEIGHT) break; if (dirmenu[i]) #define type (UINT8)(dirmenu[i][DIR_TYPE]) { if (type & EXT_LOADED) { flags |= V_TRANSLUCENT; V_DrawSmallScaledPatch(x-(16+4), y, V_TRANSLUCENT, addonsp[(type & ~EXT_LOADED)]); V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[NUM_EXT+2]); } else V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[(type & ~EXT_LOADED)]); if ((size_t)i == dir_on[menudepthleft]) { V_DrawFixedPatch((x-(16+4))< (charsonside*2 + 3)) V_DrawString(x, y+4, flags, va("%.*s...%s", charsonside, dirmenu[i]+DIR_STRING, dirmenu[i]+DIR_STRING+dirmenu[i][DIR_LEN]-(charsonside+1))); #undef charsonside else V_DrawString(x, y+4, flags, dirmenu[i]+DIR_STRING); } #undef type y += 16; } if (m != (ssize_t)sizedirmenu) V_DrawString(19, y-12 + (skullAnimCounter/5), highlightflags, "\x1B"); y = BASEVIDHEIGHT - currentMenu->y + 1; M_DrawTextBox(x - (21 + 5), y, MAXSTRINGLENGTH, 1); if (menusearch[0]) V_DrawString(x - 18, y + 8, V_ALLOWLOWERCASE, menusearch+1); else V_DrawString(x - 18, y + 8, V_ALLOWLOWERCASE|V_TRANSLUCENT, "Type to search..."); if (skullAnimCounter < 4) V_DrawCharacter(x - 18 + V_StringWidth(menusearch+1, 0), y + 8, '_' | 0x80, false); x -= (21 + 5 + 16); V_DrawSmallScaledPatch(x, y + 4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+3]); x = BASEVIDWIDTH - x - 16; V_DrawSmallScaledPatch(x, y + 4, ((!majormods) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]); if (modifiedgame) V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]); } static void M_AddonExec(INT32 ch) { if (ch != 'y' && ch != KEY_ENTER) return; S_StartSound(NULL, sfx_zoom); COM_BufAddText(va("exec \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING)); } #define len menusearch[0] static boolean M_ChangeStringAddons(INT32 choice) { if (shiftdown && choice >= 32 && choice <= 127) choice = shiftxform[choice]; switch (choice) { case KEY_DEL: if (len) { len = menusearch[1] = 0; return true; } break; case KEY_BACKSPACE: if (len) { menusearch[1+--len] = 0; return true; } break; default: if (choice >= 32 && choice <= 127) { if (len < MAXSTRINGLENGTH - 1) { menusearch[1+len++] = (char)choice; menusearch[1+len] = 0; return true; } } break; } return false; } #undef len static void M_HandleAddons(INT32 choice) { boolean exitmenu = false; // exit to previous menu if (M_ChangeStringAddons(choice)) { char *tempname = NULL; if (dirmenu && dirmenu[dir_on[menudepthleft]]) tempname = Z_StrDup(dirmenu[dir_on[menudepthleft]]+DIR_STRING); // don't need to I_Error if can't make - not important, just QoL #if 0 // much slower if (!preparefilemenu(true, false)) { UNEXIST; return; } #else // streamlined searchfilemenu(tempname); #endif } switch (choice) { case KEY_DOWNARROW: if (dir_on[menudepthleft] < sizedirmenu-1) dir_on[menudepthleft]++; S_StartSound(NULL, sfx_menu1); break; case KEY_UPARROW: if (dir_on[menudepthleft]) dir_on[menudepthleft]--; S_StartSound(NULL, sfx_menu1); break; case KEY_PGDN: { UINT8 i; for (i = numaddonsshown; i && (dir_on[menudepthleft] < sizedirmenu-1); i--) dir_on[menudepthleft]++; } S_StartSound(NULL, sfx_menu1); break; case KEY_PGUP: { UINT8 i; for (i = numaddonsshown; i && (dir_on[menudepthleft]); i--) dir_on[menudepthleft]--; } S_StartSound(NULL, sfx_menu1); break; case KEY_ENTER: { boolean refresh = true; if (!dirmenu[dir_on[menudepthleft]]) S_StartSound(NULL, sfx_s26d); else { switch (dirmenu[dir_on[menudepthleft]][DIR_TYPE]) { case EXT_FOLDER: strcpy(&menupath[menupathindex[menudepthleft]],dirmenu[dir_on[menudepthleft]]+DIR_STRING); if (menudepthleft) { menupathindex[--menudepthleft] = strlen(menupath); menupath[menupathindex[menudepthleft]] = 0; if (!preparefilemenu(false, false)) { S_StartSound(NULL, sfx_s224); M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING); menupath[menupathindex[++menudepthleft]] = 0; if (!preparefilemenu(true, false)) { UNEXIST; return; } } else { S_StartSound(NULL, sfx_menu1); dir_on[menudepthleft] = 1; } refresh = false; } else { S_StartSound(NULL, sfx_s26d); M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING); menupath[menupathindex[menudepthleft]] = 0; } break; case EXT_UP: S_StartSound(NULL, sfx_menu1); menupath[menupathindex[++menudepthleft]] = 0; if (!preparefilemenu(false, false)) { UNEXIST; return; } break; case EXT_TXT: M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press 'Y' to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),M_AddonExec,MM_YESNO); break; case EXT_CFG: M_AddonExec(KEY_ENTER); break; case EXT_LUA: #ifndef HAVE_BLUA S_StartSound(NULL, sfx_s26d); M_StartMessage(va("%c%s\x80\nThis version of SRB2Kart does not\nhave support for .lua files.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),NULL,MM_NOTHING); break; #endif // else intentional fallthrough case EXT_SOC: case EXT_WAD: #ifdef USE_KART case EXT_KART: #endif case EXT_PK3: COM_BufAddText(va("addfile \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING)); break; default: S_StartSound(NULL, sfx_s26d); } } if (refresh) refreshdirmenu |= REFRESHDIR_NORMAL; } break; case KEY_ESCAPE: exitmenu = true; break; default: break; } if (exitmenu) { closefilemenu(true); // Secret menu! //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } // ---- REPLAY HUT ----- menudemo_t *demolist; #define DF_ENCORE 0x40 static INT16 replayOn = 0; static INT16 replayScrollTitle = 0; static INT8 replayScrollDelay = TICRATE, replayScrollDir = 1; static boolean inreplayhut = false; static void PrepReplayList(void) { size_t i; if (demolist) Z_Free(demolist); demolist = Z_Calloc(sizeof(menudemo_t) * sizedirmenu, PU_STATIC, NULL); for (i = 0; i < sizedirmenu; i++) { if (dirmenu[i][DIR_TYPE] == EXT_FOLDER) { demolist[i].type = MD_SUBDIR; strncpy(demolist[i].title, dirmenu[i] + DIR_STRING, 64); } else { demolist[i].type = MD_NOTLOADED; snprintf(demolist[i].filepath, 255, "%s%s", menupath, dirmenu[i] + DIR_STRING); sprintf(demolist[i].title, "....."); } } } static void M_ReplayHut(INT32 choice) { (void)choice; if (!inreplayhut) { snprintf(menupath, 1024, "%s"PATHSEP"replay"PATHSEP"online"PATHSEP, srb2home); menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath); } inreplayhut = true; if (!preparefilemenu(false, true)) { M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING); return; } else dir_on[menudepthleft] = 0; PrepReplayList(); M_SetupNextMenu(&MISC_ReplayHutDef); G_SetGamestate(GS_TIMEATTACK); S_ChangeMusicInternal("replst", true); } static void M_HandleReplayHutList(INT32 choice) { switch (choice) { case KEY_UPARROW: if (replayOn) replayOn--; else M_PrevOpt(); S_StartSound(NULL, sfx_menu1); replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; break; case KEY_DOWNARROW: if (replayOn < (INT16)sizedirmenu-1) replayOn++; else itemOn = 0; // Not M_NextOpt because that would take us to the extra dummy item S_StartSound(NULL, sfx_menu1); replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; break; case KEY_ESCAPE: M_QuitReplayHut(); break; case KEY_ENTER: M_ClearMenus(false); demo.loadfiles = true; demo.ignorefiles = false; //@TODO prompt G_DoPlayDemo(demolist[replayOn].filepath); break; } } static void M_DrawReplayHut(void) { INT32 x, y, cursory = 0; INT16 i; INT16 replaylistitem = currentMenu->numitems-2; boolean processed_one_this_frame = false; static UINT16 replayhutmenuy = 0; V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); // Draw menu choices x = currentMenu->x; y = currentMenu->y; if (itemOn > replaylistitem) { itemOn = replaylistitem; replayOn = sizedirmenu-1; replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; } else if (itemOn < replaylistitem) { replayOn = 0; replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; } if (itemOn == replaylistitem) { INT32 maxy; // Scroll menu items if needed cursory = y + currentMenu->menuitems[replaylistitem].alphaKey + replayOn*10; maxy = y + currentMenu->menuitems[replaylistitem].alphaKey + sizedirmenu*10; if (cursory > maxy - 20) cursory = maxy - 20; if (cursory - replayhutmenuy > 150) replayhutmenuy += (cursory-150-replayhutmenuy + 1)/2; else if (cursory - replayhutmenuy < 110) replayhutmenuy += (max(0, cursory-110)-replayhutmenuy - 1)/2; } else replayhutmenuy /= 2; y -= replayhutmenuy; // Draw static menu items for (i = 0; i < replaylistitem; i++) { if (i == itemOn) cursory = y + currentMenu->menuitems[i].alphaKey; if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) V_DrawString(x, y + currentMenu->menuitems[i].alphaKey, 0, currentMenu->menuitems[i].text); else V_DrawString(x, y + currentMenu->menuitems[i].alphaKey, highlightflags, currentMenu->menuitems[i].text); } y += currentMenu->menuitems[currentMenu->numitems-1].alphaKey; for (i = 0; i < (INT16)sizedirmenu; i++) { INT32 localy = y+i*10; if (localy < 65) continue; if (localy >= 200) break; if (demolist[i].type == MD_NOTLOADED && !processed_one_this_frame) { processed_one_this_frame = true; G_LoadDemoInfo(&demolist[i]); } if (itemOn == replaylistitem && i == replayOn) { cursory = localy; if (replayScrollDelay) replayScrollDelay--; else if (replayScrollDir > 0) { if (replayScrollTitle < (V_StringWidth(demolist[i].title, 0) - (BASEVIDWIDTH - (x<<1)))<<1) replayScrollTitle++; else { replayScrollDelay = TICRATE; replayScrollDir = -1; } } else { if (replayScrollTitle > 0) replayScrollTitle--; else { replayScrollDelay = TICRATE; replayScrollDir = 1; } } V_DrawString(x - (replayScrollTitle>>1), localy, highlightflags|V_ALLOWLOWERCASE, demolist[i].title); } else V_DrawString(x, localy, V_ALLOWLOWERCASE, demolist[i].title); } // Draw the cursor V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); V_DrawString(currentMenu->x, cursory, highlightflags, currentMenu->menuitems[itemOn].text); // Now draw some replay info! V_DrawFill(10, 10, 300, 60, 239); if (itemOn == replaylistitem) { switch (demolist[replayOn].type) { case MD_NOTLOADED: V_DrawCenteredString(160, 40, 0, "Loading replay information..."); break; case MD_INVALID: V_DrawCenteredString(160, 40, warningflags, "This replay cannot be played."); break; case MD_SUBDIR: break; // Can't think of anything to draw here right now case MD_OUTDATED: V_DrawThinString(17, 60, V_ALLOWLOWERCASE|V_TRANSLUCENT|highlightflags, "Recorded on an outdated version."); /*fallthru*/ default: { // Draw level stuff lumpnum_t lumpnum; patch_t *PictureOfLevel; INT32 w, h; x = 15; y = 15; //INT32 x, y, w, i, oldval, trans, dupadjust = ((vid.width/vid.dupx) - BASEVIDWIDTH)>>1; // A 160x100 image of the level as entry MAPxxP CONS_Printf("%d %s\n", demolist[replayOn].map, G_BuildMapName(demolist[replayOn].map)); lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(demolist[replayOn].map))); if (lumpnum != LUMPERROR) PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); else PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); if (!(demolist[replayOn].kartspeed & DF_ENCORE)) V_DrawSmallScaledPatch(x, y, 0, PictureOfLevel); else { w = SHORT(PictureOfLevel->width); h = SHORT(PictureOfLevel->height); V_DrawFixedPatch((x+w)<>ANGLETOFINESHIFT); V_DrawFixedPatch((x+w/2)<width), y+20, 0, facewantprefix[demolist[replayOn].winnerskin], colormap); } } break; } } } static boolean M_QuitReplayHut(void) { // D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate. menuactive = false; D_StartTitle(); if (demolist) Z_Free(demolist); demolist = NULL; inreplayhut = false; return true; } static void M_EnterReplayOptions(INT32 choice) { (void)choice; // We can't just use M_SetupNextMenu because that'll boot us back to the title screen! currentMenu = &MISC_ReplayOptionsDef; itemOn = currentMenu->lastOn; } static void M_PandorasBox(INT32 choice) { (void)choice; CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].health - 1, 0)); CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives); CV_StealthSetValue(&cv_dummycontinues, players[consoleplayer].continues); M_SetupNextMenu(&SR_PandoraDef); } static boolean M_ExitPandorasBox(void) { if (cv_dummyrings.value != max(players[consoleplayer].health - 1, 0)) COM_ImmedExecute(va("setrings %d", cv_dummyrings.value)); if (cv_dummylives.value != players[consoleplayer].lives) COM_ImmedExecute(va("setlives %d", cv_dummylives.value)); if (cv_dummycontinues.value != players[consoleplayer].continues) COM_ImmedExecute(va("setcontinues %d", cv_dummycontinues.value)); return true; } static void M_ChangeLevel(INT32 choice) { char mapname[6]; (void)choice; strlcpy(mapname, G_BuildMapName(cv_nextmap.value), sizeof (mapname)); strlwr(mapname); mapname[5] = '\0'; M_ClearMenus(true); COM_BufAddText(va("map %s -gametype \"%s\"\n", mapname, cv_newgametype.string)); } static void M_ConfirmSpectate(INT32 choice) { (void)choice; // We allow switching to spectator even if team changing is not allowed M_ClearMenus(true); COM_ImmedExecute("changeteam spectator"); } static void M_ConfirmEnterGame(INT32 choice) { (void)choice; if (!cv_allowteamchange.value) { M_StartMessage(M_GetText("The server is not allowing\nteam changes at this time.\nPress a key.\n"), NULL, MM_NOTHING); return; } M_ClearMenus(true); COM_ImmedExecute("changeteam playing"); } static void M_ConfirmTeamScramble(INT32 choice) { (void)choice; M_ClearMenus(true); COM_ImmedExecute(va("teamscramble %d", cv_dummyscramble.value+1)); } static void M_ConfirmTeamChange(INT32 choice) { (void)choice; if (cv_dummymenuplayer.value > splitscreen+1) return; if (!cv_allowteamchange.value && cv_dummyteam.value) { M_StartMessage(M_GetText("The server is not allowing\nteam changes at this time.\nPress a key.\n"), NULL, MM_NOTHING); return; } M_ClearMenus(true); switch (cv_dummymenuplayer.value) { case 1: default: COM_ImmedExecute(va("changeteam %s", cv_dummyteam.string)); break; case 2: COM_ImmedExecute(va("changeteam2 %s", cv_dummyteam.string)); break; case 3: COM_ImmedExecute(va("changeteam3 %s", cv_dummyteam.string)); break; case 4: COM_ImmedExecute(va("changeteam4 %s", cv_dummyteam.string)); break; } } static void M_ConfirmSpectateChange(INT32 choice) { (void)choice; if (cv_dummymenuplayer.value > splitscreen+1) return; if (!cv_allowteamchange.value && cv_dummyspectate.value) { M_StartMessage(M_GetText("The server is not allowing\nteam changes at this time.\nPress a key.\n"), NULL, MM_NOTHING); return; } M_ClearMenus(true); switch (cv_dummymenuplayer.value) { case 1: default: COM_ImmedExecute(va("changeteam %s", cv_dummyspectate.string)); break; case 2: COM_ImmedExecute(va("changeteam2 %s", cv_dummyspectate.string)); break; case 3: COM_ImmedExecute(va("changeteam3 %s", cv_dummyspectate.string)); break; case 4: COM_ImmedExecute(va("changeteam4 %s", cv_dummyspectate.string)); break; } } static void M_Options(INT32 choice) { (void)choice; // if the player is not admin or server, disable gameplay & server options OP_MainMenu[5].status = OP_MainMenu[6].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // if the player is playing _at all_, disable the erase data & credits options OP_MainMenu[9].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); OP_MainMenu[10].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); OP_GameOptionsMenu[3].status = (M_SecretUnlocked(SECRET_ENCORE)) ? (IT_CVAR|IT_STRING) : IT_SECRET; // cv_kartencore OP_MainDef.prevMenu = currentMenu; M_SetupNextMenu(&OP_MainDef); } static void M_Manual(INT32 choice) { (void)choice; MISC_HelpDef.prevMenu = (choice == INT32_MAX ? NULL : currentMenu); M_SetupNextMenu(&MISC_HelpDef); } static void M_RetryResponse(INT32 ch) { if (ch != 'y' && ch != KEY_ENTER) return; if (!&players[consoleplayer] || netgame || multiplayer) // Should never happen! return; M_ClearMenus(true); G_SetRetryFlag(); } static void M_Retry(INT32 choice) { (void)choice; M_StartMessage(M_GetText("Start this race over?\n\n(Press 'Y' to confirm)\n"),M_RetryResponse,MM_YESNO); } static void M_SelectableClearMenus(INT32 choice) { (void)choice; M_ClearMenus(true); } // ====== // CHEATS // ====== static void M_UltimateCheat(INT32 choice) { (void)choice; I_Quit(); } static void M_GetAllEmeralds(INT32 choice) { (void)choice; emeralds = ((EMERALD7)*2)-1; M_StartMessage(M_GetText("You now have all 7 emeralds.\nUse them wisely.\nWith great power comes great ring drain.\n"),NULL,MM_NOTHING); G_SetGameModified(multiplayer, true); } static void M_DestroyRobotsResponse(INT32 ch) { if (ch != 'y' && ch != KEY_ENTER) return; // Destroy all robots P_DestroyRobots(); G_SetGameModified(multiplayer, true); } static void M_DestroyRobots(INT32 choice) { (void)choice; M_StartMessage(M_GetText("Do you want to destroy all\nrobots in the current level?\n\n(Press 'Y' to confirm)\n"),M_DestroyRobotsResponse,MM_YESNO); } /*static void M_LevelSelectWarp(INT32 choice) { boolean fromloadgame = (currentMenu == &SP_LevelSelectDef); (void)choice; if (W_CheckNumForName(G_BuildMapName(cv_nextmap.value)) == LUMPERROR) { // CONS_Alert(CONS_WARNING, "Internal game map '%s' not found\n", G_BuildMapName(cv_nextmap.value)); return; } startmap = (INT16)(cv_nextmap.value); fromlevelselect = true; if (fromloadgame) G_LoadGame((UINT32)cursaveslot, startmap); else { cursaveslot = -1; M_SetupChoosePlayer(0); } }*/ // ======== // SKY ROOM // ======== UINT8 skyRoomMenuTranslations[MAXUNLOCKABLES]; static char *M_GetConditionString(condition_t cond) { switch(cond.type) { case UC_PLAYTIME: return va("Play for %i:%02i:%02i", G_TicsToHours(cond.requirement), G_TicsToMinutes(cond.requirement, false), G_TicsToSeconds(cond.requirement)); case UC_MATCHESPLAYED: return va("Play %d matches", cond.requirement); case UC_GAMECLEAR: if (cond.requirement > 1) return va("Beat game %d times", cond.requirement); else return va("Beat the game"); case UC_ALLEMERALDS: if (cond.requirement > 1) return va("Beat game w/ all emeralds %d times", cond.requirement); else return va("Beat game w/ all emeralds"); case UC_OVERALLTIME: return va("Get overall time of %i:%02i:%02i", G_TicsToHours(cond.requirement), G_TicsToMinutes(cond.requirement, false), G_TicsToSeconds(cond.requirement)); case UC_MAPVISITED: return va("Visit %s", G_BuildMapTitle(cond.requirement-1)); case UC_MAPBEATEN: return va("Beat %s", G_BuildMapTitle(cond.requirement-1)); case UC_MAPALLEMERALDS: return va("Beat %s w/ all emeralds", G_BuildMapTitle(cond.requirement-1)); case UC_MAPTIME: return va("Beat %s in %i:%02i.%02i", G_BuildMapTitle(cond.extrainfo1-1), G_TicsToMinutes(cond.requirement, true), G_TicsToSeconds(cond.requirement), G_TicsToCentiseconds(cond.requirement)); case UC_TOTALEMBLEMS: return va("Get %d medals", cond.requirement); case UC_EXTRAEMBLEM: return va("Get \"%s\" medal", extraemblems[cond.requirement-1].name); default: return NULL; } } #define NUMCHECKLIST 23 static void M_DrawChecklist(void) { UINT32 i, line = 0, c; INT32 lastid; boolean secret = false; for (i = 0; i < MAXUNLOCKABLES; i++) { const char *secretname; secret = (!M_Achieved(unlockables[i].showconditionset - 1) && !unlockables[i].unlocked); if (unlockables[i].name[0] == 0 || unlockables[i].nochecklist || !unlockables[i].conditionset || unlockables[i].conditionset > MAXCONDITIONSETS || (unlockables[i].type == SECRET_HELLATTACK && secret)) // TODO: turn this into an unlockable setting instead of tying it to Hell Attack continue; ++line; secretname = M_CreateSecretMenuOption(unlockables[i].name); V_DrawString(8, (line*8), V_RETURN8|(unlockables[i].unlocked ? recommendedflags : warningflags), (secret ? secretname : unlockables[i].name)); if (conditionSets[unlockables[i].conditionset - 1].numconditions) { c = 0; lastid = -1; for (c = 0; c < conditionSets[unlockables[i].conditionset - 1].numconditions; c++) { condition_t cond = conditionSets[unlockables[i].conditionset - 1].condition[c]; UINT8 achieved = M_CheckCondition(&cond); char *str = M_GetConditionString(cond); const char *secretstr = M_CreateSecretMenuOption(str); if (!str) continue; ++line; if (lastid == -1 || cond.id != (UINT32)lastid) { V_DrawString(16, (line*8), V_MONOSPACE|V_ALLOWLOWERCASE|(achieved ? highlightflags : 0), "*"); V_DrawString(32, (line*8), V_MONOSPACE|V_ALLOWLOWERCASE|(achieved ? highlightflags : 0), (secret ? secretstr : str)); } else { V_DrawString(32, (line*8), V_MONOSPACE|V_ALLOWLOWERCASE|(achieved ? highlightflags : 0), (secret ? "?" : "&")); V_DrawString(48, (line*8), V_MONOSPACE|V_ALLOWLOWERCASE|(achieved ? highlightflags : 0), (secret ? secretstr : str)); } lastid = cond.id; } } ++line; if (line >= NUMCHECKLIST) break; } } #undef NUMCHECKLIST #define NUMHINTS 5 static void M_EmblemHints(INT32 choice) { (void)choice; SR_EmblemHintMenu[0].status = (M_SecretUnlocked(SECRET_ITEMFINDER)) ? (IT_CVAR|IT_STRING) : (IT_SECRET); M_SetupNextMenu(&SR_EmblemHintDef); itemOn = 1; // always start on back. } static void M_DrawEmblemHints(void) { INT32 i, j = 0; UINT32 collected = 0; emblem_t *emblem; const char *hint; for (i = 0; i < numemblems; i++) { emblem = &emblemlocations[i]; if (emblem->level != gamemap || emblem->type > ET_SKIN) continue; if (emblem->collected) { collected = recommendedflags; V_DrawMappedPatch(12, 12+(28*j), 0, W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE)); } else { collected = 0; V_DrawScaledPatch(12, 12+(28*j), 0, W_CachePatchName("NEEDIT", PU_CACHE)); } if (emblem->hint[0]) hint = emblem->hint; else hint = M_GetText("No hints available."); hint = V_WordWrap(40, BASEVIDWIDTH-12, 0, hint); V_DrawString(40, 8+(28*j), V_RETURN8|V_ALLOWLOWERCASE|collected, hint); if (++j >= NUMHINTS) break; } if (!j) V_DrawCenteredString(160, 48, highlightflags, "No hidden medals on this map."); M_DrawGenericMenu(); } static void M_DrawSkyRoom(void) { INT32 i, y = 0; INT32 lengthstring = 0; M_DrawGenericMenu(); if (currentMenu == &OP_SoundOptionsDef) { V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y+currentMenu->menuitems[0].alphaKey, (sound_disabled ? warningflags : highlightflags), (sound_disabled ? "OFF" : "ON")); V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y+currentMenu->menuitems[2].alphaKey, (digital_disabled ? warningflags : highlightflags), (digital_disabled ? "OFF" : "ON")); /*V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y+currentMenu->menuitems[5].alphaKey, (midi_disabled ? warningflags : highlightflags), (midi_disabled ? "OFF" : "ON"));*/ if (itemOn == 0) lengthstring = 8*(sound_disabled ? 3 : 2); else if (itemOn == 2) lengthstring = 8*(digital_disabled ? 3 : 2); /*else if (itemOn == 5) lengthstring = 8*(midi_disabled ? 3 : 2);*/ } for (i = 0; i < currentMenu->numitems; ++i) { if (currentMenu->menuitems[i].itemaction == M_HandleSoundTest) { y = currentMenu->menuitems[i].alphaKey; break; } } if (y) { y += currentMenu->y; V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, y, highlightflags, cv_soundtest.string); if (cv_soundtest.value) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, y + 8, highlightflags, S_sfx[cv_soundtest.value].name); if (i == itemOn) lengthstring = V_StringWidth(cv_soundtest.string, 0); } if (lengthstring) { V_DrawCharacter(BASEVIDWIDTH - currentMenu->x - 10 - lengthstring - (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].alphaKey, '\x1C' | highlightflags, false); // left arrow V_DrawCharacter(BASEVIDWIDTH - currentMenu->x + 2 + (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].alphaKey, '\x1D' | highlightflags, false); // right arrow } } static void M_HandleSoundTest(INT32 choice) { boolean exitmenu = false; // exit to previous menu switch (choice) { case KEY_DOWNARROW: M_NextOpt(); S_StartSound(NULL, sfx_menu1); break; case KEY_UPARROW: M_PrevOpt(); S_StartSound(NULL, sfx_menu1); break; case KEY_BACKSPACE: case KEY_ESCAPE: exitmenu = true; break; case KEY_RIGHTARROW: CV_AddValue(&cv_soundtest, 1); break; case KEY_LEFTARROW: CV_AddValue(&cv_soundtest, -1); break; case KEY_ENTER: S_StopSounds(); S_StartSound(NULL, cv_soundtest.value); break; default: break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } // Entering secrets menu /*static void M_SecretsMenu(INT32 choice) { INT32 i, j, ul; UINT8 done[MAXUNLOCKABLES]; UINT16 curheight; (void)choice; // Clear all before starting for (i = 1; i < MAXUNLOCKABLES+1; ++i) SR_MainMenu[i].status = IT_DISABLED; memset(skyRoomMenuTranslations, 0, sizeof(skyRoomMenuTranslations)); memset(done, 0, sizeof(done)); for (i = 1; i < MAXUNLOCKABLES+1; ++i) { curheight = UINT16_MAX; ul = -1; // Autosort unlockables for (j = 0; j < MAXUNLOCKABLES; ++j) { if (!unlockables[j].height || done[j] || unlockables[j].type < 0) continue; if (unlockables[j].height < curheight) { curheight = unlockables[j].height; ul = j; } } if (ul < 0) break; done[ul] = true; skyRoomMenuTranslations[i-1] = (UINT8)ul; SR_MainMenu[i].text = unlockables[ul].name; SR_MainMenu[i].alphaKey = (UINT8)unlockables[ul].height; if (unlockables[ul].type == SECRET_HEADER) { SR_MainMenu[i].status = IT_HEADER; continue; } SR_MainMenu[i].status = IT_SECRET; if (unlockables[ul].unlocked) { switch (unlockables[ul].type) { case SECRET_LEVELSELECT: SR_MainMenu[i].status = IT_STRING|IT_CALL; SR_MainMenu[i].itemaction = M_CustomLevelSelect; break; case SECRET_WARP: SR_MainMenu[i].status = IT_STRING|IT_CALL; SR_MainMenu[i].itemaction = M_CustomWarp; break; case SECRET_CREDITS: SR_MainMenu[i].status = IT_STRING|IT_CALL; SR_MainMenu[i].itemaction = M_Credits; break; case SECRET_SOUNDTEST: SR_MainMenu[i].status = IT_STRING|IT_KEYHANDLER; SR_MainMenu[i].itemaction = M_HandleSoundTest; default: break; } } } M_SetupNextMenu(&SR_MainDef); }*/ // ================== // NEW GAME FUNCTIONS // ================== /*INT32 ultimate_selectable = false; static void M_NewGame(void) { fromlevelselect = false; startmap = spstage_start; CV_SetValue(&cv_newgametype, GT_RACE); // SRB2kart M_SetupChoosePlayer(0); }*/ /*static void M_CustomWarp(INT32 choice) { INT32 ul = skyRoomMenuTranslations[choice-1]; startmap = (INT16)(unlockables[ul].variable); M_SetupChoosePlayer(0); }*/ static void M_Credits(INT32 choice) { (void)choice; cursaveslot = -2; M_ClearMenus(true); F_StartCredits(); } /*static void M_CustomLevelSelect(INT32 choice) { INT32 ul = skyRoomMenuTranslations[choice-1]; SR_LevelSelectDef.prevMenu = currentMenu; levellistmode = LLM_LEVELSELECT; maplistoption = (UINT8)(unlockables[ul].variable); if (M_CountLevelsToShowInList() == 0) { M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); return; } M_PrepareLevelSelect(); M_SetupNextMenu(&SR_LevelSelectDef); }*/ // ================== // SINGLE PLAYER MENU // ================== #if 0 // Bring this back when we have actual single-player static void M_SinglePlayerMenu(INT32 choice) { (void)choice; SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; /*SP_MainMenu[spnightsmode].status = (M_SecretUnlocked(SECRET_NIGHTSMODE)) ? IT_CALL|IT_STRING : IT_SECRET;*/ M_SetupNextMenu(&SP_MainDef); } #endif /*static void M_LoadGameLevelSelect(INT32 choice) { (void)choice; levellistmode = LLM_LEVELSELECT; maplistoption = 1; if (M_CountLevelsToShowInList() == 0) { M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); return; } SP_LevelSelectDef.prevMenu = currentMenu; M_PrepareLevelSelect(); M_SetupNextMenu(&SP_LevelSelectDef); }*/ // ============== // LOAD GAME MENU // ============== /*static INT32 saveSlotSelected = 0; static short menumovedir = 0; static void M_DrawLoadGameData(void) { INT32 ecks; INT32 i; ecks = SP_LoadDef.x + 24; M_DrawTextBox(SP_LoadDef.x-12,144, 24, 4); if (saveSlotSelected == NOSAVESLOT) // last slot is play without saving { if (ultimate_selectable) { V_DrawCenteredString(ecks + 68, 144, V_ORANGEMAP, "ULTIMATE MODE"); V_DrawCenteredString(ecks + 68, 156, 0, "NO RINGS, NO ONE-UPS,"); V_DrawCenteredString(ecks + 68, 164, 0, "NO CONTINUES, ONE LIFE,"); V_DrawCenteredString(ecks + 68, 172, 0, "FINAL DESTINATION."); } else { V_DrawCenteredString(ecks + 68, 144, V_ORANGEMAP, "PLAY WITHOUT SAVING"); V_DrawCenteredString(ecks + 68, 156, 0, "THIS GAME WILL NOT BE"); V_DrawCenteredString(ecks + 68, 164, 0, "SAVED, BUT YOU CAN STILL"); V_DrawCenteredString(ecks + 68, 172, 0, "GET MEDALS AND SECRETS."); } return; } if (savegameinfo[saveSlotSelected].lives == -42) // Empty { V_DrawCenteredString(ecks + 68, 160, 0, "NO DATA"); return; } if (savegameinfo[saveSlotSelected].lives == -666) // savegame is bad { V_DrawCenteredString(ecks + 68, 144, warningflags, "CORRUPT SAVE FILE"); V_DrawCenteredString(ecks + 68, 156, 0, "THIS SAVE FILE"); V_DrawCenteredString(ecks + 68, 164, 0, "CAN NOT BE LOADED."); V_DrawCenteredString(ecks + 68, 172, 0, "DELETE USING BACKSPACE."); return; } // Draw the back sprite, it looks ugly if we don't V_DrawScaledPatch(SP_LoadDef.x, 144+8, 0, livesback); if (savegameinfo[saveSlotSelected].skincolor == 0) V_DrawScaledPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE)); else { UINT8 *colormap = R_GetTranslationColormap(savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor, GTC_MENUCACHE); V_DrawMappedPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE), colormap); } V_DrawString(ecks + 12, 152, 0, savegameinfo[saveSlotSelected].playername); #ifdef SAVEGAMES_OTHERVERSIONS if (savegameinfo[saveSlotSelected].gamemap & 16384) V_DrawCenteredString(ecks + 68, 144, warningflags, "OUTDATED SAVE FILE!"); #endif if (savegameinfo[saveSlotSelected].gamemap & 8192) V_DrawString(ecks + 12, 160, recommendedflags, "CLEAR!"); else V_DrawString(ecks + 12, 160, 0, va("%s", savegameinfo[saveSlotSelected].levelname)); // Use the big face pic for lives, duh. :3 V_DrawScaledPatch(ecks + 12, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX)); V_DrawTallNum(ecks + 40, 172, 0, savegameinfo[saveSlotSelected].lives); // Absolute ridiculousness, condensed into another function. V_DrawContinueIcon(ecks + 58, 182, 0, savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor); V_DrawScaledPatch(ecks + 68, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX)); V_DrawTallNum(ecks + 96, 172, 0, savegameinfo[saveSlotSelected].continues); for (i = 0; i < 7; ++i) { if (savegameinfo[saveSlotSelected].numemeralds & (1 << i)) V_DrawScaledPatch(ecks + 104 + (i * 8), 172, 0, tinyemeraldpics[i]); } } #define LOADBARHEIGHT SP_LoadDef.y + (LINEHEIGHT * (j+1)) + ymod #define CURSORHEIGHT SP_LoadDef.y + (LINEHEIGHT*3) - 1 static void M_DrawLoad(void) { INT32 i, j; INT32 ymod = 0, offset = 0; M_DrawMenuTitle(); if (menumovedir != 0) //movement illusion { ymod = (-(LINEHEIGHT/4))*menumovedir; offset = ((menumovedir > 0) ? -1 : 1); } V_DrawCenteredString(BASEVIDWIDTH/2, 40, 0, "Press backspace to delete a save."); for (i = MAXSAVEGAMES + saveSlotSelected - 2 + offset, j = 0;i <= MAXSAVEGAMES + saveSlotSelected + 2 + offset; i++, j++) { if ((menumovedir < 0 && j == 4) || (menumovedir > 0 && j == 0)) continue; //this helps give the illusion of movement M_DrawSaveLoadBorder(SP_LoadDef.x, LOADBARHEIGHT); if ((i%MAXSAVEGAMES) == NOSAVESLOT) // play without saving { if (ultimate_selectable) V_DrawCenteredString(SP_LoadDef.x+92, LOADBARHEIGHT - 1, V_ORANGEMAP, "ULTIMATE MODE"); else V_DrawCenteredString(SP_LoadDef.x+92, LOADBARHEIGHT - 1, V_ORANGEMAP, "PLAY WITHOUT SAVING"); continue; } if (savegameinfo[i%MAXSAVEGAMES].lives == -42) V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_TRANSLUCENT, "NO DATA"); else if (savegameinfo[i%MAXSAVEGAMES].lives == -666) V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, warningflags, "CORRUPT SAVE FILE"); else if (savegameinfo[i%MAXSAVEGAMES].gamemap & 8192) V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, recommendedflags, "CLEAR!"); else V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, 0, va("%s", savegameinfo[i%MAXSAVEGAMES].levelname)); //Draw the save slot number on the right side V_DrawRightAlignedString(SP_LoadDef.x+192, LOADBARHEIGHT - 1, 0, va("%d",(i%MAXSAVEGAMES) + 1)); } //Draw cursors on both sides. V_DrawScaledPatch( 32, CURSORHEIGHT, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); V_DrawScaledPatch(274, CURSORHEIGHT, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); M_DrawLoadGameData(); //finishing the movement illusion if (menumovedir) menumovedir += ((menumovedir > 0) ? 1 : -1); if (abs(menumovedir) > 3) menumovedir = 0; } #undef LOADBARHEIGHT #undef CURSORHEIGHT // // User wants to load this game // static void M_LoadSelect(INT32 choice) { (void)choice; if (saveSlotSelected == NOSAVESLOT) //last slot is play without saving { M_NewGame(); cursaveslot = -1; return; } if (!FIL_ReadFileOK(va(savegamename, saveSlotSelected))) { // This slot is empty, so start a new game here. M_NewGame(); } else if (savegameinfo[saveSlotSelected].gamemap & 8192) // Completed M_LoadGameLevelSelect(saveSlotSelected + 1); else G_LoadGame((UINT32)saveSlotSelected, 0); cursaveslot = saveSlotSelected; } #define VERSIONSIZE 16 #define BADSAVE { savegameinfo[slot].lives = -666; Z_Free(savebuffer); return; } #define CHECKPOS if (save_p >= end_p) BADSAVE // Reads the save file to list lives, level, player, etc. // Tails 05-29-2003 static void M_ReadSavegameInfo(UINT32 slot) { size_t length; char savename[255]; UINT8 *savebuffer; UINT8 *end_p; // buffer end point, don't read past here UINT8 *save_p; INT32 fake; // Dummy variable char temp[sizeof(timeattackfolder)]; char vcheck[VERSIONSIZE]; #ifdef SAVEGAMES_OTHERVERSIONS boolean oldversion = false; #endif sprintf(savename, savegamename, slot); length = FIL_ReadFile(savename, &savebuffer); if (length == 0) { savegameinfo[slot].lives = -42; return; } end_p = savebuffer + length; // skip the description field save_p = savebuffer; // Version check memset(vcheck, 0, sizeof (vcheck)); sprintf(vcheck, "version %d", VERSION); if (strcmp((const char *)save_p, (const char *)vcheck)) { #ifdef SAVEGAMES_OTHERVERSIONS oldversion = true; #else BADSAVE // Incompatible versions? #endif } save_p += VERSIONSIZE; // dearchive all the modifications // P_UnArchiveMisc() CHECKPOS fake = READINT16(save_p); if (((fake-1) & 8191) >= NUMMAPS) BADSAVE if(!mapheaderinfo[(fake-1) & 8191]) { savegameinfo[slot].levelname[0] = '\0'; savegameinfo[slot].actnum = 0; } else { strcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl); savegameinfo[slot].actnum = 0; //mapheaderinfo[(fake-1) & 8191]->actnum } #ifdef SAVEGAMES_OTHERVERSIONS if (oldversion) { if (fake == 24) //meh, let's count old Clear! saves too fake |= 8192; fake |= 16384; // marker for outdated version } #endif savegameinfo[slot].gamemap = fake; CHECKPOS fake = READUINT16(save_p)-357; // emeralds savegameinfo[slot].numemeralds = (UINT8)fake; CHECKPOS READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to if (strcmp(temp, timeattackfolder)) BADSAVE // P_UnArchivePlayer() CHECKPOS savegameinfo[slot].skincolor = READUINT8(save_p); CHECKPOS savegameinfo[slot].skinnum = READUINT8(save_p); CHECKPOS (void)READINT32(save_p); // Score CHECKPOS savegameinfo[slot].lives = READINT32(save_p); // lives CHECKPOS savegameinfo[slot].continues = READINT32(save_p); // continues if (fake & (1<<10)) { CHECKPOS savegameinfo[slot].botskin = READUINT8(save_p); if (savegameinfo[slot].botskin-1 >= numskins) savegameinfo[slot].botskin = 0; CHECKPOS savegameinfo[slot].botcolor = READUINT8(save_p); // because why not. } else savegameinfo[slot].botskin = 0; if (savegameinfo[slot].botskin) snprintf(savegameinfo[slot].playername, 36, "%s & %s", skins[savegameinfo[slot].skinnum].realname, skins[savegameinfo[slot].botskin-1].realname); else strcpy(savegameinfo[slot].playername, skins[savegameinfo[slot].skinnum].realname); savegameinfo[slot].playername[31] = 0; // File end marker check CHECKPOS if (READUINT8(save_p) != 0x1d) BADSAVE; // done Z_Free(savebuffer); } #undef CHECKPOS #undef BADSAVE // // M_ReadSaveStrings // read the strings from the savegame files // and put it in savegamestrings global variable // static void M_ReadSaveStrings(void) { FILE *handle; UINT32 i; char name[256]; for (i = 0; i < MAXSAVEGAMES; i++) { snprintf(name, sizeof name, savegamename, i); name[sizeof name - 1] = '\0'; handle = fopen(name, "rb"); if (handle == NULL) { savegameinfo[i].lives = -42; continue; } fclose(handle); M_ReadSavegameInfo(i); } } // // User wants to delete this game // static void M_SaveGameDeleteResponse(INT32 ch) { char name[256]; if (ch != 'y' && ch != KEY_ENTER) return; // delete savegame snprintf(name, sizeof name, savegamename, saveSlotSelected); name[sizeof name - 1] = '\0'; remove(name); // Refresh savegame menu info M_ReadSaveStrings(); } static void M_HandleLoadSave(INT32 choice) { boolean exitmenu = false; // exit to previous menu switch (choice) { case KEY_DOWNARROW: S_StartSound(NULL, sfx_menu1); ++saveSlotSelected; if (saveSlotSelected >= MAXSAVEGAMES) saveSlotSelected -= MAXSAVEGAMES; menumovedir = 1; break; case KEY_UPARROW: S_StartSound(NULL, sfx_menu1); --saveSlotSelected; if (saveSlotSelected < 0) saveSlotSelected += MAXSAVEGAMES; menumovedir = -1; break; case KEY_ENTER: S_StartSound(NULL, sfx_menu1); if (savegameinfo[saveSlotSelected].lives != -666) // don't allow loading of "bad saves" M_LoadSelect(saveSlotSelected); break; case KEY_ESCAPE: exitmenu = true; break; case KEY_BACKSPACE: S_StartSound(NULL, sfx_menu1); // Don't allow people to 'delete' "Play without Saving." // Nor allow people to 'delete' slots with no saves in them. if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected].lives != -42) M_StartMessage(M_GetText("Are you sure you want to delete\nthis save game?\n\n(Press 'Y' to confirm)\n"),M_SaveGameDeleteResponse,MM_YESNO); break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } // // Selected from SRB2 menu // static void M_LoadGame(INT32 choice) { (void)choice; M_ReadSaveStrings(); M_SetupNextMenu(&SP_LoadDef); } // // Used by cheats to force the save menu to a specific spot. // void M_ForceSaveSlotSelected(INT32 sslot) { // Already there? Out of bounds? Whatever, then! if (sslot == saveSlotSelected || sslot >= MAXSAVEGAMES) return; // Figure out whether to display up movement or down movement menumovedir = (saveSlotSelected - sslot) > 0 ? -1 : 1; if (abs(saveSlotSelected - sslot) > (MAXSAVEGAMES>>1)) menumovedir *= -1; saveSlotSelected = sslot; } // ================ // CHARACTER SELECT // ================ static void M_SetupChoosePlayer(INT32 choice) { (void)choice; if (mapheaderinfo[startmap-1] && mapheaderinfo[startmap-1]->forcecharacter[0] != '\0') { M_ChoosePlayer(0); //oh for crying out loud just get STARTED, it doesn't matter! return; } if (Playing() == false) { S_StopMusic(); S_ChangeMusicInternal("chrsel", true); } SP_PlayerDef.prevMenu = currentMenu; M_SetupNextMenu(&SP_PlayerDef); char_scroll = itemOn*128*FRACUNIT; // finish scrolling the menu Z_Free(char_notes); char_notes = NULL; } // Draw the choose player setup menu, had some fun with player anim static void M_DrawSetupChoosePlayerMenu(void) { const INT32 my = 24; patch_t *patch; INT32 i, o, j; char *picname; // Black BG V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); //V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); // Character select profile images!1 M_DrawTextBox(0, my, 16, 20); if (abs(itemOn*128*FRACUNIT - char_scroll) > 256*FRACUNIT) char_scroll = itemOn*128*FRACUNIT; else if (itemOn*128*FRACUNIT - char_scroll > 128*FRACUNIT) char_scroll += 48*FRACUNIT; else if (itemOn*128*FRACUNIT - char_scroll < -128*FRACUNIT) char_scroll -= 48*FRACUNIT; else if (itemOn*128*FRACUNIT > char_scroll+16*FRACUNIT) char_scroll += 16*FRACUNIT; else if (itemOn*128*FRACUNIT < char_scroll-16*FRACUNIT) char_scroll -= 16*FRACUNIT; else // close enough. char_scroll = itemOn*128*FRACUNIT; // just be exact now. i = (char_scroll+16*FRACUNIT)/(128*FRACUNIT); o = ((char_scroll/FRACUNIT)+16)%128; // prev character if (i-1 >= 0 && PlayerMenu[i-1].status != IT_DISABLED && o < 32) { picname = description[i-1].picname; if (picname[0] == '\0') { picname = strtok(Z_StrDup(description[i-1].skinname), "&"); for (j = 0; j < numskins; j++) if (stricmp(skins[j].name, picname) == 0) { Z_Free(picname); picname = skins[j].charsel; break; } if (j == numskins) // AAAAAAAAAA picname = skins[0].charsel; } patch = W_CachePatchName(picname, PU_CACHE); if (SHORT(patch->width) >= 256) V_DrawCroppedPatch(8<height) - 64 + o*2, SHORT(patch->width), SHORT(patch->height)); else V_DrawCroppedPatch(8<height) - 32 + o, SHORT(patch->width), SHORT(patch->height)); W_UnlockCachedPatch(patch); } // next character if (i+1 < currentMenu->numitems && PlayerMenu[i+1].status != IT_DISABLED && o < 128) { picname = description[i+1].picname; if (picname[0] == '\0') { picname = strtok(Z_StrDup(description[i+1].skinname), "&"); for (j = 0; j < numskins; j++) if (stricmp(skins[j].name, picname) == 0) { Z_Free(picname); picname = skins[j].charsel; break; } if (j == numskins) // AAAAAAAAAA picname = skins[0].charsel; } patch = W_CachePatchName(picname, PU_CACHE); if (SHORT(patch->width) >= 256) V_DrawCroppedPatch(8<width), o*2); else V_DrawCroppedPatch(8<width), o); W_UnlockCachedPatch(patch); } // current character if (i < currentMenu->numitems && PlayerMenu[i].status != IT_DISABLED) { picname = description[i].picname; if (picname[0] == '\0') { picname = strtok(Z_StrDup(description[i].skinname), "&"); for (j = 0; j < numskins; j++) if (stricmp(skins[j].name, picname) == 0) { Z_Free(picname); picname = skins[j].charsel; break; } if (j == numskins) // AAAAAAAAAA picname = skins[0].charsel; } patch = W_CachePatchName(picname, PU_CACHE); if (o >= 0 && o <= 32) { if (SHORT(patch->width) >= 256) V_DrawSmallScaledPatch(8, my + 40 - o, 0, patch); else V_DrawScaledPatch(8, my + 40 - o, 0, patch); } else { if (SHORT(patch->width) >= 256) V_DrawCroppedPatch(8<width), SHORT(patch->height)); else V_DrawCroppedPatch(8<width), SHORT(patch->height)); } W_UnlockCachedPatch(patch); } // draw title (or big pic) M_DrawMenuTitle(); // Character description M_DrawTextBox(136, my, 21, 20); if (!char_notes) char_notes = V_WordWrap(0, 21*8, V_ALLOWLOWERCASE, description[itemOn].notes); V_DrawString(146, my + 9, V_RETURN8|V_ALLOWLOWERCASE, char_notes); } // Chose the player you want to use Tails 03-02-2002 static void M_ChoosePlayer(INT32 choice) { char *skin1,*skin2; INT32 skinnum; //boolean ultmode = (ultimate_selectable && SP_PlayerDef.prevMenu == &SP_LoadDef && saveSlotSelected == NOSAVESLOT); // skip this if forcecharacter if (mapheaderinfo[startmap-1] && mapheaderinfo[startmap-1]->forcecharacter[0] == '\0') { // M_SetupChoosePlayer didn't call us directly, that means we've been properly set up. char_scroll = itemOn*128*FRACUNIT; // finish scrolling the menu M_DrawSetupChoosePlayerMenu(); // draw the finally selected character one last time for the fadeout } M_ClearMenus(true); skin1 = strtok(description[choice].skinname, "&"); skin2 = strtok(NULL, "&"); if (skin2) { // this character has a second skin skinnum = R_SkinAvailable(skin1); botskin = (UINT8)(R_SkinAvailable(skin2)+1); botingame = true; botcolor = skins[botskin-1].prefcolor; // undo the strtok description[choice].skinname[strlen(skin1)] = '&'; } else { skinnum = R_SkinAvailable(description[choice].skinname); botingame = false; botskin = 0; botcolor = 0; } if (startmap != spstage_start) cursaveslot = -1; lastmapsaved = 0; gamecomplete = false; G_DeferedInitNew(false, G_BuildMapName(startmap), (UINT8)skinnum, 0, fromlevelselect); COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this }*/ // =============== // STATISTICS MENU // =============== static INT32 statsLocation; static INT32 statsMax; static INT16 statsMapList[NUMMAPS+1]; static void M_Statistics(INT32 choice) { INT16 i, j = 0; (void)choice; memset(statsMapList, 0, sizeof(statsMapList)); for (i = 0; i < NUMMAPS; i++) { if (!mapheaderinfo[i] || mapheaderinfo[i]->lvlttl[0] == '\0') continue; if (!(mapheaderinfo[i]->typeoflevel & TOL_RACE) // TOL_SP || (mapheaderinfo[i]->menuflags & (LF2_HIDEINSTATS|LF2_HIDEINMENU))) continue; if (M_MapLocked(i+1)) // !mapvisited[i] continue; statsMapList[j++] = i; } statsMapList[j] = -1; statsMax = j - 11 + numextraemblems; statsLocation = 0; if (statsMax < 0) statsMax = 0; M_SetupNextMenu(&SP_LevelStatsDef); } static void M_DrawStatsMaps(int location) { INT32 y = 80, i = -1; INT16 mnum; extraemblem_t *exemblem; boolean dotopname = true, dobottomarrow = (location < statsMax); if (location) V_DrawCharacter(10, y-(skullAnimCounter/5), '\x1A' | highlightflags, false); // up arrow while (statsMapList[++i] != -1) { if (location) { --location; continue; } else if (dotopname) { V_DrawString(20, y, highlightflags, "LEVEL NAME"); V_DrawString(256, y, highlightflags, "MEDALS"); y += 8; dotopname = false; } mnum = statsMapList[i]; M_DrawMapEmblems(mnum+1, 295, y); if (mapheaderinfo[mnum]->levelflags & LF_NOZONE) V_DrawString(20, y, 0, va("%s %s", mapheaderinfo[mnum]->lvlttl, mapheaderinfo[mnum]->actnum)); else V_DrawString(20, y, 0, va("%s %s %s", mapheaderinfo[mnum]->lvlttl, (mapheaderinfo[mnum]->zonttl[0] ? mapheaderinfo[mnum]->zonttl : "ZONE"), mapheaderinfo[mnum]->actnum)); y += 8; if (y >= BASEVIDHEIGHT-8) goto bottomarrow; } if (dotopname && !location) { V_DrawString(20, y, highlightflags, "LEVEL NAME"); V_DrawString(256, y, highlightflags, "MEDALS"); y += 8; } else if (location) --location; // Extra Emblems for (i = -2; i < numextraemblems; ++i) { if (i == -1) { V_DrawString(20, y, highlightflags, "EXTRA MEDALS"); if (location) { y += 8; location++; } } if (location) { --location; continue; } if (i >= 0) { exemblem = &extraemblems[i]; if (exemblem->collected) V_DrawSmallMappedPatch(295, y, 0, W_CachePatchName(M_GetExtraEmblemPatch(exemblem), PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, M_GetExtraEmblemColor(exemblem), GTC_MENUCACHE)); else V_DrawSmallScaledPatch(295, y, 0, W_CachePatchName("NEEDIT", PU_CACHE)); V_DrawString(20, y, 0, va("%s", exemblem->description)); } y += 8; if (y >= BASEVIDHEIGHT-8) goto bottomarrow; } bottomarrow: if (dobottomarrow) V_DrawCharacter(10, y-8 + (skullAnimCounter/5), '\x1B' | highlightflags, false); // down arrow } static void M_DrawLevelStats(void) { char beststr[40]; tic_t besttime = 0; INT32 i; INT32 mapsunfinished = 0; M_DrawMenuTitle(); V_DrawString(20, 24, highlightflags, "Total Play Time:"); V_DrawCenteredString(BASEVIDWIDTH/2, 32, 0, va("%i hours, %i minutes, %i seconds", G_TicsToHours(totalplaytime), G_TicsToMinutes(totalplaytime, false), G_TicsToSeconds(totalplaytime))); V_DrawString(20, 42, highlightflags, "Total Matches:"); V_DrawRightAlignedString(BASEVIDWIDTH-16, 42, 0, va("%i played", matchesplayed)); for (i = 0; i < NUMMAPS; i++) { if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK)) continue; if (!mainrecords[i] || mainrecords[i]->time <= 0) { mapsunfinished++; continue; } besttime += mainrecords[i]->time; } V_DrawString(20, 62, highlightflags, "Combined time records:"); sprintf(beststr, "%i:%02i:%02i.%02i", G_TicsToHours(besttime), G_TicsToMinutes(besttime, false), G_TicsToSeconds(besttime), G_TicsToCentiseconds(besttime)); V_DrawRightAlignedString(BASEVIDWIDTH-16, 62, (mapsunfinished ? warningflags : 0), beststr); if (mapsunfinished) V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, warningflags, va("(%d unfinished)", mapsunfinished)); else V_DrawRightAlignedString(BASEVIDWIDTH-16, 70, recommendedflags, "(complete)"); V_DrawString(32, 70, 0, va("x %d/%d", M_CountEmblems(), numemblems+numextraemblems)); V_DrawSmallScaledPatch(20, 70, 0, W_CachePatchName("GOTITA", PU_STATIC)); M_DrawStatsMaps(statsLocation); } // Handle statistics. static void M_HandleLevelStats(INT32 choice) { boolean exitmenu = false; // exit to previous menu switch (choice) { case KEY_DOWNARROW: S_StartSound(NULL, sfx_menu1); if (statsLocation < statsMax) ++statsLocation; break; case KEY_UPARROW: S_StartSound(NULL, sfx_menu1); if (statsLocation) --statsLocation; break; case KEY_PGDN: S_StartSound(NULL, sfx_menu1); statsLocation += (statsLocation+13 >= statsMax) ? statsMax-statsLocation : 13; break; case KEY_PGUP: S_StartSound(NULL, sfx_menu1); statsLocation -= (statsLocation < 13) ? statsLocation : 13; break; case KEY_ESCAPE: exitmenu = true; break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } // =========== // MODE ATTACK // =========== // Drawing function for Time Attack void M_DrawTimeAttackMenu(void) { INT32 i, x, y, cursory = 0; UINT16 dispstatus; //S_ChangeMusicInternal("racent", true); // Eww, but needed for when user hits escape during demo playback V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); M_DrawMenuTitle(); if (currentMenu == &SP_TimeAttackDef) M_DrawLevelSelectOnly(true, false); // draw menu (everything else goes on top of it) // Sadly we can't just use generic mode menus because we need some extra hacks x = currentMenu->x; y = currentMenu->y; // Character face! if (W_CheckNumForName(skins[cv_chooseskin.value-1].facewant) != LUMPERROR) { UINT8 *colormap = R_GetTranslationColormap(cv_chooseskin.value-1, cv_playercolor.value, GTC_MENUCACHE); V_DrawMappedPatch(BASEVIDWIDTH-x - SHORT(facewantprefix[cv_chooseskin.value-1]->width), y, 0, facewantprefix[cv_chooseskin.value-1], colormap); } for (i = 0; i < currentMenu->numitems; ++i) { dispstatus = (currentMenu->menuitems[i].status & IT_DISPLAY); if (dispstatus != IT_STRING && dispstatus != IT_WHITESTRING) continue; y = currentMenu->y+currentMenu->menuitems[i].alphaKey; if (i == itemOn) cursory = y; V_DrawString(x, y, (dispstatus == IT_WHITESTRING) ? highlightflags : 0 , currentMenu->menuitems[i].text); // Cvar specific handling if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_CVAR) { consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; if (currentMenu->menuitems[i].status & IT_CV_STRING) { M_DrawTextBox(x + 32, y - 8, MAXPLAYERNAME, 1); V_DrawString(x + 40, y, V_ALLOWLOWERCASE, cv->string); if (itemOn == i && skullAnimCounter < 4) // blink cursor V_DrawCharacter(x + 40 + V_StringWidth(cv->string, V_ALLOWLOWERCASE), y, '_',false); } else { const char *str = ((cv == &cv_chooseskin) ? skins[cv_chooseskin.value-1].realname : cv->string); INT32 soffset = 40, strw = V_StringWidth(str, 0); // hack to keep the menu from overlapping the level icon if (currentMenu != &SP_TimeAttackDef || cv == &cv_nextmap) soffset = 0; // Should see nothing but strings V_DrawString(BASEVIDWIDTH - x - soffset - strw, y, highlightflags, str); if (i == itemOn) { V_DrawCharacter(BASEVIDWIDTH - x - soffset - 10 - strw - (skullAnimCounter/5), y, '\x1C' | highlightflags, false); // left arrow V_DrawCharacter(BASEVIDWIDTH - x - soffset + 2 + (skullAnimCounter/5), y, '\x1D' | highlightflags, false); // right arrow } } } else if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_KEYHANDLER && cv_dummystaff.value) // bad hacky assumption: IT_KEYHANDLER is assumed to be staff ghost selector { INT32 strw = V_StringWidth(dummystaffname, V_ALLOWLOWERCASE); V_DrawString(BASEVIDWIDTH - x - strw, y, highlightflags|V_ALLOWLOWERCASE, dummystaffname); if (i == itemOn) { V_DrawCharacter(BASEVIDWIDTH - x - 10 - strw - (skullAnimCounter/5), y, '\x1C' | highlightflags, false); // left arrow V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y, '\x1D' | highlightflags, false); // right arrow } } } x = currentMenu->x; y = currentMenu->y; // DRAW THE SKULL CURSOR V_DrawScaledPatch(x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); V_DrawString(x, cursory, highlightflags, currentMenu->menuitems[itemOn].text); // Level record list if (cv_nextmap.value) { INT32 dupadjust = (vid.width/vid.dupx); tic_t lap = 0, time = 0; if (mainrecords[cv_nextmap.value-1]) { lap = mainrecords[cv_nextmap.value-1]->lap; time = mainrecords[cv_nextmap.value-1]->time; } V_DrawFill((BASEVIDWIDTH - dupadjust)>>1, 78, dupadjust, 36, 239); V_DrawRightAlignedString(149, 80, highlightflags, "BEST LAP:"); K_drawKartTimestamp(lap, 19, 86, 0, 2); V_DrawRightAlignedString(292, 80, highlightflags, "BEST TIME:"); K_drawKartTimestamp(time, 162, 86, cv_nextmap.value, 1); } /*{ char beststr[40]; emblem_t *em; if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->time) sprintf(beststr, "(none)"); else sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(mainrecords[cv_nextmap.value-1]->time, true), G_TicsToSeconds(mainrecords[cv_nextmap.value-1]->time), G_TicsToCentiseconds(mainrecords[cv_nextmap.value-1]->time)); V_DrawString(64, y+48, highlightflags, "BEST TIME:"); V_DrawRightAlignedString(BASEVIDWIDTH - 64 - 24 - 8, y+48, V_ALLOWLOWERCASE, beststr); if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->lap) sprintf(beststr, "(none)"); else sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(mainrecords[cv_nextmap.value-1]->lap, true), G_TicsToSeconds(mainrecords[cv_nextmap.value-1]->lap), G_TicsToCentiseconds(mainrecords[cv_nextmap.value-1]->lap)); V_DrawString(64, y+56, highlightflags, "BEST LAP:"); V_DrawRightAlignedString(BASEVIDWIDTH - 64 - 24 - 8, y+56, V_ALLOWLOWERCASE, beststr); // Draw record emblems. em = M_GetLevelEmblems(cv_nextmap.value); while (em) { switch (em->type) { case ET_TIME: break; default: goto skipThisOne; } if (em->collected) V_DrawMappedPatch(BASEVIDWIDTH - 64 - 24, y+48, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_MENUCACHE)); else V_DrawScaledPatch(BASEVIDWIDTH - 64 - 24, y+48, 0, W_CachePatchName("NEEDIT", PU_CACHE)); skipThisOne: em = M_GetLevelEmblems(-1); } }*/ // ALWAYS DRAW player name, level name, skin and color even when not on this menu! if (currentMenu != &SP_TimeAttackDef) { consvar_t *ncv; for (i = 0; i < 4; ++i) { y = currentMenu->y+SP_TimeAttackMenu[i].alphaKey; V_DrawString(x, y, V_TRANSLUCENT, SP_TimeAttackMenu[i].text); ncv = (consvar_t *)SP_TimeAttackMenu[i].itemaction; if (SP_TimeAttackMenu[i].status & IT_CV_STRING) { M_DrawTextBox(x + 32, y - 8, MAXPLAYERNAME, 1); V_DrawString(x + 40, y, V_TRANSLUCENT|V_ALLOWLOWERCASE, ncv->string); } else { const char *str = ((ncv == &cv_chooseskin) ? skins[cv_chooseskin.value-1].realname : ncv->string); INT32 soffset = 40, strw = V_StringWidth(str, 0); // hack to keep the menu from overlapping the level icon if (ncv == &cv_nextmap) soffset = 0; // Should see nothing but strings V_DrawString(BASEVIDWIDTH - x - soffset - strw, y, highlightflags|V_TRANSLUCENT, str); } } } } // Going to Time Attack menu... static void M_TimeAttack(INT32 choice) { (void)choice; memset(skins_cons_t, 0, sizeof (skins_cons_t)); levellistmode = LLM_RECORDATTACK; // Don't be dependent on cv_newgametype if (M_CountLevelsToShowInList() == 0) { M_StartMessage(M_GetText("No record-attackable levels found.\n"),NULL,MM_NOTHING); return; } M_PatchSkinNameTable(); M_PrepareLevelSelect(); M_SetupNextMenu(&SP_TimeAttackDef); G_SetGamestate(GS_TIMEATTACK); if (cv_nextmap.value) Nextmap_OnChange(); else CV_AddValue(&cv_nextmap, 1); itemOn = tastart; // "Start" is selected. S_ChangeMusicInternal("racent", true); } static boolean M_QuitTimeAttackMenu(void) { // you know what? always putting these in the buffer won't hurt anything. COM_BufAddText(va("skin \"%s\"\n", cv_chooseskin.string)); return true; } // Drawing function for Nights Attack /*void M_DrawNightsAttackMenu(void) { patch_t *PictureOfLevel; lumpnum_t lumpnum; char beststr[40]; S_ChangeMusicInternal("racent", true); // Eww, but needed for when user hits escape during demo playback V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); // draw menu (everything else goes on top of it) M_DrawGenericMenu(); // A 160x100 image of the level as entry MAPxxP lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); if (lumpnum != LUMPERROR) PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); else PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); V_DrawSmallScaledPatch(90, 28, 0, PictureOfLevel); // Level record list if (cv_nextmap.value) { emblem_t *em; INT32 yHeight; UINT8 bestoverall = G_GetBestNightsGrade(cv_nextmap.value, 0); UINT8 bestgrade = G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value); UINT32 bestscore = G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value); tic_t besttime = G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value); if (P_HasGrades(cv_nextmap.value, 0)) V_DrawScaledPatch(200, 28 + 8, 0, ngradeletters[bestoverall]); if (currentMenu == &SP_NightsAttackDef) { if (P_HasGrades(cv_nextmap.value, cv_dummymares.value)) { V_DrawString(160-88, 112, highlightflags, "BEST GRADE:"); V_DrawSmallScaledPatch(160 + 86 - (ngradeletters[bestgrade]->width/2), 112 + 8 - (ngradeletters[bestgrade]->height/2), 0, ngradeletters[bestgrade]); } if (!bestscore) sprintf(beststr, "(none)"); else sprintf(beststr, "%u", bestscore); V_DrawString(160 - 88, 122, highlightflags, "BEST SCORE:"); V_DrawRightAlignedString(160 + 88, 122, V_ALLOWLOWERCASE, beststr); if (besttime == UINT32_MAX) sprintf(beststr, "(none)"); else sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(besttime, true), G_TicsToSeconds(besttime), G_TicsToCentiseconds(besttime)); V_DrawString(160-88, 132, highlightflags, "BEST TIME:"); V_DrawRightAlignedString(160+88, 132, V_ALLOWLOWERCASE, beststr); if (cv_dummymares.value == 0) { // Draw record emblems. em = M_GetLevelEmblems(cv_nextmap.value); while (em) { switch (em->type) { case ET_NGRADE: yHeight = 112; break; case ET_NTIME: yHeight = 132; break; default: goto skipThisOne; } if (em->collected) V_DrawSmallMappedPatch(160+88, yHeight, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_MENUCACHE)); else V_DrawSmallScaledPatch(160+88, yHeight, 0, W_CachePatchName("NEEDIT", PU_CACHE)); skipThisOne: em = M_GetLevelEmblems(-1); } } } // ALWAYS DRAW level name even when not on this menu! else { consvar_t *ncv; INT32 x = SP_NightsAttackDef.x; INT32 y = SP_NightsAttackDef.y; ncv = (consvar_t *)SP_NightsAttackMenu[0].itemaction; V_DrawString(x, y + SP_NightsAttackMenu[0].alphaKey, V_TRANSLUCENT, SP_NightsAttackMenu[0].text); V_DrawString(BASEVIDWIDTH - x - V_StringWidth(ncv->string, 0), y + SP_NightsAttackMenu[0].alphaKey, highlightflags|V_TRANSLUCENT, ncv->string); } } }*/ // Going to Nights Attack menu... /*static void M_NightsAttack(INT32 choice) { (void)choice; memset(skins_cons_t, 0, sizeof (skins_cons_t)); levellistmode = LLM_NIGHTSATTACK; // Don't be dependent on cv_newgametype if (M_CountLevelsToShowInList() == 0) { M_StartMessage(M_GetText("No NiGHTS-attackable levels found.\n"),NULL,MM_NOTHING); return; } // This is really just to make sure Sonic is the played character, just in case M_PatchSkinNameTable(); M_PrepareLevelSelect(); M_SetupNextMenu(&SP_NightsAttackDef); Nextmap_OnChange(); itemOn = nastart; // "Start" is selected. G_SetGamestate(GS_TIMEATTACK); S_ChangeMusicInternal("racent", true); }*/ // Player has selected the "START" from the nights attack screen /*static void M_ChooseNightsAttack(INT32 choice) { char nameofdemo[256]; (void)choice; emeralds = 0; M_ClearMenus(true); modeattacking = ATTACKING_NIGHTS; I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755); I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755); snprintf(nameofdemo, sizeof nameofdemo, "replay"PATHSEP"%s"PATHSEP"%s-last", timeattackfolder, G_BuildMapName(cv_nextmap.value)); if (!cv_autorecord.value) remove(va("%s"PATHSEP"%s.lmp", srb2home, nameofdemo)); else G_RecordDemo(nameofdemo); G_DeferedInitNew(false, G_BuildMapName(cv_nextmap.value), 0, 0, false); }*/ // Player has selected the "START" from the time attack screen static void M_ChooseTimeAttack(INT32 choice) { char *gpath; const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; char nameofdemo[256]; (void)choice; emeralds = 0; M_ClearMenus(true); modeattacking = ATTACKING_RECORD; I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755); I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755); if ((gpath = malloc(glen)) == NULL) I_Error("Out of memory for replay filepath\n"); sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, G_BuildMapName(cv_nextmap.value)); snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, cv_chooseskin.string); if (!cv_autorecord.value) remove(va("%s"PATHSEP"%s.lmp", srb2home, nameofdemo)); else G_RecordDemo(nameofdemo); G_DeferedInitNew(false, G_BuildMapName(cv_nextmap.value), (UINT8)(cv_chooseskin.value-1), 0, false); } static void M_HandleStaffReplay(INT32 choice) { boolean exitmenu = false; // exit to previous menu lumpnum_t l = W_CheckNumForName(va("%sS%02u",G_BuildMapName(cv_nextmap.value),cv_dummystaff.value)); switch (choice) { case KEY_DOWNARROW: M_NextOpt(); S_StartSound(NULL, sfx_menu1); break; case KEY_UPARROW: M_PrevOpt(); S_StartSound(NULL, sfx_menu1); break; case KEY_BACKSPACE: case KEY_ESCAPE: exitmenu = true; break; case KEY_RIGHTARROW: CV_AddValue(&cv_dummystaff, 1); S_StartSound(NULL, sfx_menu1); break; case KEY_LEFTARROW: CV_AddValue(&cv_dummystaff, -1); S_StartSound(NULL, sfx_menu1); break; case KEY_ENTER: if (l == LUMPERROR) break; M_ClearMenus(true); modeattacking = ATTACKING_RECORD; demo.loadfiles = false; demo.ignorefiles = true; // Just assume that record attack replays have the files needed G_DoPlayDemo(va("%sS%02u",G_BuildMapName(cv_nextmap.value),cv_dummystaff.value)); break; default: break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } // Player has selected the "REPLAY" from the time attack screen static void M_ReplayTimeAttack(INT32 choice) { const char *which; M_ClearMenus(true); modeattacking = ATTACKING_RECORD; // set modeattacking before G_DoPlayDemo so the map loader knows demo.loadfiles = false; demo.ignorefiles = true; // Just assume that record attack replays have the files needed if (currentMenu == &SP_ReplayDef) { switch(choice) { default: case 0: // best time which = "time-best"; break; case 1: // best lap which = "lap-best"; break; case 2: // last which = "last"; break; case 3: // guest // srb2/replay/main/map01-guest.lmp G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value))); return; } // srb2/replay/main/map01-sonic-time-best.lmp G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), cv_chooseskin.string, which)); } /*else if (currentMenu == &SP_NightsReplayDef) { switch(choice) { default: case 0: // best score which = "score-best"; break; case 1: // best time which = "time-best"; break; case 2: // last which = "last"; break; case 3: // staff return; // M_HandleStaffReplay case 4: // guest which = "guest"; break; } // srb2/replay/main/map01-score-best.lmp G_DoPlayDemo(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), which)); }*/ } static void M_EraseGuest(INT32 choice) { const char *rguest = va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); (void)choice; if (FIL_FileExists(rguest)) remove(rguest); /*if (currentMenu == &SP_NightsGuestReplayDef) M_SetupNextMenu(&SP_NightsAttackDef); else*/ M_SetupNextMenu(&SP_TimeAttackDef); CV_AddValue(&cv_nextmap, -1); CV_AddValue(&cv_nextmap, 1); M_StartMessage(M_GetText("Guest replay data erased.\n"),NULL,MM_NOTHING); } static void M_OverwriteGuest(const char *which) { char *rguest = Z_StrDup(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value))); UINT8 *buf; size_t len; len = FIL_ReadFile(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-%s-%s.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value), cv_chooseskin.string, which), &buf); if (!len) { return; } if (FIL_FileExists(rguest)) { M_StopMessage(0); remove(rguest); } FIL_WriteFile(rguest, buf, len); Z_Free(rguest); /*if (currentMenu == &SP_NightsGuestReplayDef) M_SetupNextMenu(&SP_NightsAttackDef); else*/ M_SetupNextMenu(&SP_TimeAttackDef); CV_AddValue(&cv_nextmap, -1); CV_AddValue(&cv_nextmap, 1); M_StartMessage(M_GetText("Guest replay data saved.\n"),NULL,MM_NOTHING); } static void M_OverwriteGuest_Time(INT32 choice) { (void)choice; M_OverwriteGuest("time-best"); } static void M_OverwriteGuest_Lap(INT32 choice) { (void)choice; M_OverwriteGuest("lap-best"); } /* SRB2Kart static void M_OverwriteGuest_Score(INT32 choice) { (void)choice; M_OverwriteGuest("score-best"); } static void M_OverwriteGuest_Rings(INT32 choice) { (void)choice; M_OverwriteGuest("rings-best"); }*/ static void M_OverwriteGuest_Last(INT32 choice) { (void)choice; M_OverwriteGuest("last"); } static void M_SetGuestReplay(INT32 choice) { void (*which)(INT32); switch(choice) { case 0: // best time which = M_OverwriteGuest_Time; break; case 1: // best lap which = M_OverwriteGuest_Lap; break; case 2: // last which = M_OverwriteGuest_Last; break; case 3: // guest default: M_StartMessage(M_GetText("Are you sure you want to\ndelete the guest replay data?\n\n(Press 'Y' to confirm)\n"),M_EraseGuest,MM_YESNO); return; } if (FIL_FileExists(va("%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s-guest.lmp", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)))) M_StartMessage(M_GetText("Are you sure you want to\noverwrite the guest replay data?\n\n(Press 'Y' to confirm)\n"),which,MM_YESNO); else which(0); } static void M_ModeAttackRetry(INT32 choice) { (void)choice; G_CheckDemoStatus(); // Cancel recording if (modeattacking == ATTACKING_RECORD) M_ChooseTimeAttack(0); /*else if (modeattacking == ATTACKING_NIGHTS) M_ChooseNightsAttack(0);*/ } static void M_ModeAttackEndGame(INT32 choice) { (void)choice; G_CheckDemoStatus(); // Cancel recording if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) Command_ExitGame_f(); M_StartControlPanel(); switch(modeattacking) { default: case ATTACKING_RECORD: currentMenu = &SP_TimeAttackDef; break; /*case ATTACKING_NIGHTS: currentMenu = &SP_NightsAttackDef; break;*/ } itemOn = currentMenu->lastOn; G_SetGamestate(GS_TIMEATTACK); modeattacking = ATTACKING_NONE; S_ChangeMusicInternal("racent", true); // Update replay availability. CV_AddValue(&cv_nextmap, 1); CV_AddValue(&cv_nextmap, -1); } // ======== // END GAME // ======== static void M_ExitGameResponse(INT32 ch) { if (ch != 'y' && ch != KEY_ENTER) return; //Command_ExitGame_f(); G_SetExitGameFlag(); M_ClearMenus(true); } static void M_EndGame(INT32 choice) { (void)choice; if (demo.playback) return; if (!Playing()) return; M_StartMessage(M_GetText("Are you sure you want to end the game?\n\n(Press 'Y' to confirm)\n"), M_ExitGameResponse, MM_YESNO); } //=========================================================================== // Connect Menu //=========================================================================== #define SERVERHEADERHEIGHT 44 #define SERVERLINEHEIGHT 12 #define S_LINEY(n) currentMenu->y + SERVERHEADERHEIGHT + (n * SERVERLINEHEIGHT) #ifndef NONET static UINT32 localservercount; static void M_HandleServerPage(INT32 choice) { boolean exitmenu = false; // exit to previous menu switch (choice) { case KEY_DOWNARROW: M_NextOpt(); S_StartSound(NULL, sfx_menu1); break; case KEY_UPARROW: M_PrevOpt(); S_StartSound(NULL, sfx_menu1); break; case KEY_BACKSPACE: case KEY_ESCAPE: exitmenu = true; break; case KEY_ENTER: case KEY_RIGHTARROW: S_StartSound(NULL, sfx_menu1); if ((serverlistpage + 1) * SERVERS_PER_PAGE < serverlistcount) serverlistpage++; break; case KEY_LEFTARROW: S_StartSound(NULL, sfx_menu1); if (serverlistpage > 0) serverlistpage--; break; default: break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } static void M_Connect(INT32 choice) { // do not call menuexitfunc M_ClearMenus(false); COM_BufAddText(va("connect node %d\n", serverlist[choice-FIRSTSERVERLINE + serverlistpage * SERVERS_PER_PAGE].node)); } static void M_Refresh(INT32 choice) { (void)choice; // Display a little "please wait" message. M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "Searching for servers..."); V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait."); I_OsPolling(); I_UpdateNoBlit(); if (rendermode == render_soft) I_FinishUpdate(); // page flip or blit buffer // note: this is the one case where 0 is a valid room number // because it corresponds to "All" CL_UpdateServerList(!(ms_RoomId < 0), ms_RoomId); // first page of servers serverlistpage = 0; } static INT32 menuRoomIndex = 0; static void M_DrawRoomMenu(void) { const char *rmotd; // use generic drawer for cursor, items and title M_DrawGenericMenu(); V_DrawString(currentMenu->x - 16, currentMenu->y, highlightflags, M_GetText("Select a room")); M_DrawTextBox(144, 24, 20, 20); if (itemOn == 0) rmotd = M_GetText("Don't connect to the Master Server."); else rmotd = room_list[itemOn-1].motd; rmotd = V_WordWrap(0, 20*8, 0, rmotd); V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd); } static void M_DrawConnectMenu(void) { UINT16 i, j; const char *gt = "Unknown"; const char *spd = ""; INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE; for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++) MP_ConnectMenu[i].status = IT_STRING | IT_SPACE; if (!numPages) numPages = 1; // Room name if (ms_RoomId < 0) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey, highlightflags, (itemOn == mp_connect_room) ? "" : ""); else V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, highlightflags, room_list[menuRoomIndex].name); #undef mp_server_room } #endif } static void M_MapChange(INT32 choice) { (void)choice; levellistmode = LLM_CREATESERVER; CV_SetValue(&cv_newgametype, gametype); CV_SetValue(&cv_nextmap, gamemap); M_PrepareLevelSelect(); M_SetupNextMenu(&MISC_ChangeLevelDef); } static void M_StartOfflineServerMenu(INT32 choice) { (void)choice; levellistmode = LLM_CREATESERVER; M_PrepareLevelSelect(); M_SetupNextMenu(&MP_OfflineServerDef); } #ifndef NONET static void M_StartServerMenu(INT32 choice) { (void)choice; levellistmode = LLM_CREATESERVER; M_PrepareLevelSelect(); ms_RoomId = -1; M_SetupNextMenu(&MP_ServerDef); } // ============== // CONNECT VIA IP // ============== static char setupm_ip[28]; #endif static UINT8 setupm_pselect = 1; // Draw the funky Connect IP menu. Tails 11-19-2002 // So much work for such a little thing! static void M_DrawMPMainMenu(void) { INT32 x = currentMenu->x; INT32 y = currentMenu->y; // use generic drawer for cursor, items and title M_DrawGenericMenu(); #ifndef NONET #if MAXPLAYERS != 16 Update the maxplayers label... #endif V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[4].alphaKey, ((itemOn == 4) ? highlightflags : 0), "(2-16 players)"); #endif V_DrawRightAlignedString(BASEVIDWIDTH-x, y+MP_MainMenu[5].alphaKey, ((itemOn == 5) ? highlightflags : 0), "(2-4 players)" ); #ifndef NONET y += MP_MainMenu[8].alphaKey; V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 239); // draw name string V_DrawString(x+8,y+12, V_ALLOWLOWERCASE, setupm_ip); // draw text cursor for name if (itemOn == 8 && skullAnimCounter < 4) //blink cursor V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_ALLOWLOWERCASE),y+12,'_',false); #endif // character bar, ripped off the color bar :V { #define iconwidth 32 #define spacingwidth 32 #define incrwidth (iconwidth + spacingwidth) UINT8 i = 0, pskin, pcol; // player arrangement width, but there's also a chance i'm a furry, shhhhhh const INT32 paw = iconwidth + 3*incrwidth; INT32 trans = 0; UINT8 *colmap; x = BASEVIDWIDTH/2 - paw/2; y = currentMenu->y + 32; while (++i <= 4) { switch (i) { default: pskin = R_SkinAvailable(cv_skin.string); pcol = cv_playercolor.value; break; case 2: pskin = R_SkinAvailable(cv_skin2.string); pcol = cv_playercolor2.value; break; case 3: pskin = R_SkinAvailable(cv_skin3.string); pcol = cv_playercolor3.value; break; case 4: pskin = R_SkinAvailable(cv_skin4.string); pcol = cv_playercolor4.value; break; } if (pskin >= MAXSKINS) pskin = 0; if (!trans && i > cv_splitplayers.value) trans = V_TRANSLUCENT; colmap = R_GetTranslationColormap(pskin, pcol, GTC_MENUCACHE); V_DrawFixedPatch(x< 7) cursorframe = 0; V_DrawFixedPatch(x< 1) { if (--setupm_pselect < 1) setupm_pselect = cv_splitplayers.value; S_StartSound(NULL,sfx_menu1); // Tails } break; case KEY_RIGHTARROW: if (cv_splitplayers.value > 1) { if (++setupm_pselect > cv_splitplayers.value) setupm_pselect = 1; S_StartSound(NULL,sfx_menu1); // Tails } break; case KEY_DOWNARROW: M_NextOpt(); S_StartSound(NULL,sfx_menu1); // Tails break; case KEY_UPARROW: M_PrevOpt(); S_StartSound(NULL,sfx_menu1); // Tails break; case KEY_ENTER: { S_StartSound(NULL,sfx_menu1); // Tails currentMenu->lastOn = itemOn; switch (setupm_pselect) { case 2: M_SetupMultiPlayer2(0); return; case 3: M_SetupMultiPlayer3(0); return; case 4: M_SetupMultiPlayer4(0); return; default: M_SetupMultiPlayer(0); return; } break; } case KEY_ESCAPE: exitmenu = true; break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu (currentMenu->prevMenu); else M_ClearMenus(true); } } #ifndef NONET // Tails 11-19-2002 static void M_ConnectIP(INT32 choice) { (void)choice; if (*setupm_ip == 0) { M_StartMessage("You must specify an IP address.\n", NULL, MM_NOTHING); return; } M_ClearMenus(true); COM_BufAddText(va("connect \"%s\"\n", setupm_ip)); // A little "please wait" message. M_DrawTextBox(56, BASEVIDHEIGHT/2-12, 24, 2); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "Connecting to server..."); I_OsPolling(); I_UpdateNoBlit(); if (rendermode == render_soft) I_FinishUpdate(); // page flip or blit buffer } // Tails 11-19-2002 static void M_HandleConnectIP(INT32 choice) { size_t l; boolean exitmenu = false; // exit to previous menu and send name change switch (choice) { case KEY_DOWNARROW: M_NextOpt(); S_StartSound(NULL,sfx_menu1); // Tails break; case KEY_UPARROW: M_PrevOpt(); S_StartSound(NULL,sfx_menu1); // Tails break; case KEY_ENTER: S_StartSound(NULL,sfx_menu1); // Tails currentMenu->lastOn = itemOn; M_ConnectIP(1); break; case KEY_ESCAPE: exitmenu = true; break; case KEY_BACKSPACE: if ((l = strlen(setupm_ip)) != 0) { S_StartSound(NULL,sfx_menu1); // Tails setupm_ip[l-1] = 0; } break; case KEY_DEL: if (setupm_ip[0]) { S_StartSound(NULL,sfx_menu1); // Tails setupm_ip[0] = 0; } break; default: l = strlen(setupm_ip); if (l >= 28-1) break; // Rudimentary number and period enforcing - also allows letters so hostnames can be used instead if ((choice >= '-' && choice <= ':') || (choice >= 'A' && choice <= 'Z') || (choice >= 'a' && choice <= 'z')) { S_StartSound(NULL,sfx_menu1); // Tails setupm_ip[l] = (char)choice; setupm_ip[l+1] = 0; } else if (choice >= 199 && choice <= 211 && choice != 202 && choice != 206) //numpad too! { char keypad_translation[] = {'7','8','9','-','4','5','6','+','1','2','3','0','.'}; choice = keypad_translation[choice - 199]; S_StartSound(NULL,sfx_menu1); // Tails setupm_ip[l] = (char)choice; setupm_ip[l+1] = 0; } break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu (currentMenu->prevMenu); else M_ClearMenus(true); } } #endif //!NONET // ======================== // MULTIPLAYER PLAYER SETUP // ======================== // Tails 03-02-2002 static INT32 multi_tics; static state_t *multi_state; // this is set before entering the MultiPlayer setup menu, // for either player 1 or 2 static char setupm_name[MAXPLAYERNAME+1]; static player_t *setupm_player; static consvar_t *setupm_cvskin; static consvar_t *setupm_cvcolor; static consvar_t *setupm_cvname; static INT32 setupm_fakeskin; static INT32 setupm_fakecolor; static void M_DrawSetupMultiPlayerMenu(void) { INT32 mx, my, st, flags = 0; spritedef_t *sprdef; spriteframe_t *sprframe; patch_t *statbg = W_CachePatchName("K_STATBG", PU_CACHE); patch_t *statlr = W_CachePatchName("K_STATLR", PU_CACHE); patch_t *statud = W_CachePatchName("K_STATUD", PU_CACHE); patch_t *statdot = W_CachePatchName("K_SDOT0", PU_CACHE); patch_t *patch; UINT8 frame; UINT8 speed; UINT8 weight; UINT8 i; const UINT8 *flashcol = V_GetStringColormap(highlightflags); INT32 statx, staty; mx = MP_PlayerSetupDef.x; my = MP_PlayerSetupDef.y; statx = (BASEVIDWIDTH - mx - 118); staty = (my+62); // use generic drawer for cursor, items and title M_DrawGenericMenu(); // draw name string M_DrawTextBox(mx + 32, my - 8, MAXPLAYERNAME, 1); V_DrawString(mx + 40, my, V_ALLOWLOWERCASE, setupm_name); // draw text cursor for name if (!itemOn && skullAnimCounter < 4) // blink cursor V_DrawCharacter(mx + 40 + V_StringWidth(setupm_name, V_ALLOWLOWERCASE), my, '_',false); // draw skin string st = V_StringWidth(skins[setupm_fakeskin].realname, 0); V_DrawString(BASEVIDWIDTH - mx - st, my + 16, ((MP_PlayerSetupMenu[2].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|highlightflags|V_ALLOWLOWERCASE, skins[setupm_fakeskin].realname); if (itemOn == 1) { V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 16, '\x1C' | highlightflags, false); // left arrow V_DrawCharacter(BASEVIDWIDTH - mx + 2 + (skullAnimCounter/5), my + 16, '\x1D' | highlightflags, false); // right arrow } // draw the name of the color you have chosen // Just so people don't go thinking that "Default" is Green. st = V_StringWidth(KartColor_Names[setupm_fakecolor], 0); V_DrawString(BASEVIDWIDTH - mx - st, my + 152, highlightflags|V_ALLOWLOWERCASE, KartColor_Names[setupm_fakecolor]); // SRB2kart if (itemOn == 2) { V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 152, '\x1C' | highlightflags, false); // left arrow V_DrawCharacter(BASEVIDWIDTH - mx + 2 + (skullAnimCounter/5), my + 152, '\x1D' | highlightflags, false); // right arrow } // SRB2Kart: draw the stat backer // labels V_DrawThinString(statx+16, staty, V_6WIDTHSPACE|highlightflags, "Acceleration"); V_DrawThinString(statx+91, staty, V_6WIDTHSPACE|highlightflags, "Max Speed"); V_DrawThinString(statx, staty+12, V_6WIDTHSPACE|highlightflags, "Handling"); V_DrawThinString(statx+7, staty+77, V_6WIDTHSPACE|highlightflags, "Weight"); // label arrows V_DrawFixedPatch((statx+64)<= MAXSKINCOLORS) col -= MAXSKINCOLORS-1; x += w; } } #undef indexwidth // character bar, ripped off the color bar :V if (setupm_fakecolor) // inverse should never happen #define iconwidth 32 { const INT32 icons = 4; INT32 k = -icons; INT16 col = setupm_fakeskin - icons; INT32 x = BASEVIDWIDTH/2 - ((icons+1)*24) - 4; fixed_t scale = FRACUNIT/2; INT32 offx = 8, offy = 8; patch_t *cursor; static UINT8 cursorframe = 0; patch_t *face; UINT8 *colmap; if (skullAnimCounter % 4 == 0) cursorframe++; if (cursorframe > 7) cursorframe = 0; cursor = W_CachePatchName(va("K_BHILI%d", cursorframe+1), PU_CACHE); if (col < 0) col += numskins; while (k <= icons) { if (!(k++)) { scale = FRACUNIT; face = facewantprefix[col]; offx = 12; offy = 0; } else { scale = FRACUNIT/2; face = facerankprefix[col]; offx = 8; offy = 8; } colmap = R_GetTranslationColormap(col, setupm_fakecolor, GTC_MENUCACHE); V_DrawFixedPatch((x+offx)<= numskins) col -= numskins; x += FixedMul(iconwidth<nextstate; if (st != S_NULL) multi_state = &states[st]; multi_tics = multi_state->tics; if (multi_tics == -1) multi_tics = 15; } // skin 0 is default player sprite if (R_SkinAvailable(skins[setupm_fakeskin].name) != -1) sprdef = &skins[R_SkinAvailable(skins[setupm_fakeskin].name)].spritedef; else sprdef = &skins[0].spritedef; if (!sprdef->numframes) // No frames ?? return; // Can't render! frame = multi_state->frame & FF_FRAMEMASK; if (frame >= sprdef->numframes) // Walking animation missing frame = 0; // Try to use standing frame sprframe = &sprdef->spriteframes[frame]; patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE); if (sprframe->flip & 1) // Only for first sprite flags |= V_FLIP; // This sprite is left/right flipped! // draw box around guy V_DrawFill(mx + 43 - (charw/2), my+65, charw, 84, 239); // draw player sprite if (setupm_fakecolor) // inverse should never happen { UINT8 *colormap = R_GetTranslationColormap(setupm_fakeskin, setupm_fakecolor, GTC_MENUCACHE); if (skins[setupm_fakeskin].flags & SF_HIRES) { V_DrawFixedPatch((mx+43)< 127 || itemOn != 0) break; l = strlen(setupm_name); if (l < MAXPLAYERNAME) { S_StartSound(NULL,sfx_menu1); // Tails setupm_name[l] =(char)choice; setupm_name[l+1] =0; } break; } // check skin if (setupm_fakeskin < 0) setupm_fakeskin = numskins-1; if (setupm_fakeskin > numskins-1) setupm_fakeskin = 0; // check color if (setupm_fakecolor < 1) setupm_fakecolor = MAXSKINCOLORS-1; if (setupm_fakecolor > MAXSKINCOLORS-1) setupm_fakecolor = 1; if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu (currentMenu->prevMenu); else M_ClearMenus(true); } } // start the multiplayer setup menu static void M_SetupMultiPlayer(INT32 choice) { (void)choice; multi_state = &states[mobjinfo[MT_PLAYER].seestate]; multi_tics = multi_state->tics; strcpy(setupm_name, cv_playername.string); // set for player 1 setupm_player = &players[consoleplayer]; setupm_cvskin = &cv_skin; setupm_cvcolor = &cv_playercolor; setupm_cvname = &cv_playername; // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); if (setupm_fakeskin == -1) setupm_fakeskin = 0; setupm_fakecolor = setupm_cvcolor->value; // disable skin changes if we can't actually change skins if (!CanChangeSkin(consoleplayer)) MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT); else MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING); MP_PlayerSetupDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_PlayerSetupDef); } // start the multiplayer setup menu, for secondary player (splitscreen mode) static void M_SetupMultiPlayer2(INT32 choice) { (void)choice; multi_state = &states[mobjinfo[MT_PLAYER].seestate]; multi_tics = multi_state->tics; strcpy (setupm_name, cv_playername2.string); // set for splitscreen secondary player setupm_player = &players[secondarydisplayplayer]; setupm_cvskin = &cv_skin2; setupm_cvcolor = &cv_playercolor2; setupm_cvname = &cv_playername2; // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); if (setupm_fakeskin == -1) setupm_fakeskin = 0; setupm_fakecolor = setupm_cvcolor->value; // disable skin changes if we can't actually change skins if (splitscreen && !CanChangeSkin(secondarydisplayplayer)) MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT); else MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER | IT_STRING); MP_PlayerSetupDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_PlayerSetupDef); } // start the multiplayer setup menu, for third player (splitscreen mode) static void M_SetupMultiPlayer3(INT32 choice) { (void)choice; multi_state = &states[mobjinfo[MT_PLAYER].seestate]; multi_tics = multi_state->tics; strcpy(setupm_name, cv_playername3.string); // set for splitscreen third player setupm_player = &players[thirddisplayplayer]; setupm_cvskin = &cv_skin3; setupm_cvcolor = &cv_playercolor3; setupm_cvname = &cv_playername3; // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); if (setupm_fakeskin == -1) setupm_fakeskin = 0; setupm_fakecolor = setupm_cvcolor->value; // disable skin changes if we can't actually change skins if (splitscreen > 1 && !CanChangeSkin(thirddisplayplayer)) MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT); else MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER | IT_STRING); MP_PlayerSetupDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_PlayerSetupDef); } // start the multiplayer setup menu, for third player (splitscreen mode) static void M_SetupMultiPlayer4(INT32 choice) { (void)choice; multi_state = &states[mobjinfo[MT_PLAYER].seestate]; multi_tics = multi_state->tics; strcpy(setupm_name, cv_playername4.string); // set for splitscreen fourth player setupm_player = &players[fourthdisplayplayer]; setupm_cvskin = &cv_skin4; setupm_cvcolor = &cv_playercolor4; setupm_cvname = &cv_playername4; // For whatever reason this doesn't work right if you just use ->value setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string); if (setupm_fakeskin == -1) setupm_fakeskin = 0; setupm_fakecolor = setupm_cvcolor->value; // disable skin changes if we can't actually change skins if (splitscreen > 2 && !CanChangeSkin(fourthdisplayplayer)) MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT); else MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER | IT_STRING); MP_PlayerSetupDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_PlayerSetupDef); } static boolean M_QuitMultiPlayerMenu(void) { size_t l; // send name if changed if (strcmp(setupm_name, setupm_cvname->string)) { // remove trailing whitespaces for (l= strlen(setupm_name)-1; (signed)l >= 0 && setupm_name[l] ==' '; l--) setupm_name[l] =0; COM_BufAddText (va("%s \"%s\"\n",setupm_cvname->name,setupm_name)); } // you know what? always putting these in the buffer won't hurt anything. COM_BufAddText (va("%s \"%s\"\n",setupm_cvskin->name,skins[setupm_fakeskin].name)); COM_BufAddText (va("%s %d\n",setupm_cvcolor->name,setupm_fakecolor)); return true; } // ================= // DATA OPTIONS MENU // ================= static UINT8 erasecontext = 0; static void M_EraseDataResponse(INT32 ch) { if (ch != 'y' && ch != KEY_ENTER) return; S_StartSound(NULL, sfx_itrole); // bweh heh heh // Delete the data if (erasecontext == 2) { // SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets totalplaytime = 0; matchesplayed = 0; F_StartIntro(); } if (erasecontext != 1) G_ClearRecords(); if (erasecontext != 0) M_ClearSecrets(); M_ClearMenus(true); } static void M_EraseData(INT32 choice) { const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\n(Press 'Y' to confirm)\n"); erasecontext = (UINT8)choice; if (choice == 0) eschoice = M_GetText("Record Attack data"); else if (choice == 1) eschoice = M_GetText("Secrets data"); else eschoice = M_GetText("ALL game data"); M_StartMessage(va(esstr, eschoice),M_EraseDataResponse,MM_YESNO); } static void M_ScreenshotOptions(INT32 choice) { (void)choice; Screenshot_option_Onchange(); Moviemode_mode_Onchange(); M_SetupNextMenu(&OP_ScreenshotOptionsDef); } // ============= // JOYSTICK MENU // ============= // Start the controls menu, setting it up for either the console player, // or the secondary splitscreen player static void M_DrawJoystick(void) { INT32 i, compareval4, compareval3, compareval2, compareval; M_DrawGenericMenu(); for (i = 0; i < 8; i++) { M_DrawTextBox(OP_JoystickSetDef.x-8, OP_JoystickSetDef.y+LINEHEIGHT*i-12, 28, 1); //M_DrawSaveLoadBorder(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i); #ifdef JOYSTICK_HOTPLUG if (atoi(cv_usejoystick4.string) > I_NumJoys()) compareval4 = atoi(cv_usejoystick4.string); else compareval4 = cv_usejoystick4.value; if (atoi(cv_usejoystick3.string) > I_NumJoys()) compareval3 = atoi(cv_usejoystick3.string); else compareval3 = cv_usejoystick3.value; if (atoi(cv_usejoystick2.string) > I_NumJoys()) compareval2 = atoi(cv_usejoystick2.string); else compareval2 = cv_usejoystick2.value; if (atoi(cv_usejoystick.string) > I_NumJoys()) compareval = atoi(cv_usejoystick.string); else compareval = cv_usejoystick.value; #else compareval4 = cv_usejoystick4.value; compareval3 = cv_usejoystick3.value; compareval2 = cv_usejoystick2.value; compareval = cv_usejoystick.value #endif if ((setupcontrolplayer == 4 && (i == compareval4)) || (setupcontrolplayer == 3 && (i == compareval3)) || (setupcontrolplayer == 2 && (i == compareval2)) || (setupcontrolplayer == 1 && (i == compareval))) V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,joystickInfo[i]); else V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,0,joystickInfo[i]); } } void M_SetupJoystickMenu(INT32 choice) { INT32 i = 0; const char *joyNA = "Unavailable"; INT32 n = I_NumJoys(); (void)choice; strcpy(joystickInfo[i], "None"); for (i = 1; i < 8; i++) { if (i <= n && (I_GetJoyName(i)) != NULL) strncpy(joystickInfo[i], I_GetJoyName(i), 28); else strcpy(joystickInfo[i], joyNA); #ifdef JOYSTICK_HOTPLUG // We use cv_usejoystick.string as the USER-SET var // and cv_usejoystick.value as the INTERNAL var // // In practice, if cv_usejoystick.string == 0, this overrides // cv_usejoystick.value and always disables // // Update cv_usejoystick.string here so that the user can // properly change this value. if (i == cv_usejoystick.value) CV_SetValue(&cv_usejoystick, i); if (i == cv_usejoystick2.value) CV_SetValue(&cv_usejoystick2, i); if (i == cv_usejoystick3.value) CV_SetValue(&cv_usejoystick3, i); if (i == cv_usejoystick4.value) CV_SetValue(&cv_usejoystick4, i); #endif } M_SetupNextMenu(&OP_JoystickSetDef); } static void M_Setup1PJoystickMenu(INT32 choice) { setupcontrolplayer = 1; OP_JoystickSetDef.prevMenu = &OP_Joystick1Def; M_SetupJoystickMenu(choice); } static void M_Setup2PJoystickMenu(INT32 choice) { setupcontrolplayer = 2; OP_JoystickSetDef.prevMenu = &OP_Joystick2Def; M_SetupJoystickMenu(choice); } static void M_Setup3PJoystickMenu(INT32 choice) { setupcontrolplayer = 3; OP_JoystickSetDef.prevMenu = &OP_Joystick3Def; M_SetupJoystickMenu(choice); } static void M_Setup4PJoystickMenu(INT32 choice) { setupcontrolplayer = 4; OP_JoystickSetDef.prevMenu = &OP_Joystick4Def; M_SetupJoystickMenu(choice); } static void M_AssignJoystick(INT32 choice) { #ifdef JOYSTICK_HOTPLUG INT32 oldchoice, oldstringchoice; INT32 numjoys = I_NumJoys(); if (setupcontrolplayer == 4) { oldchoice = oldstringchoice = atoi(cv_usejoystick4.string) > numjoys ? atoi(cv_usejoystick4.string) : cv_usejoystick4.value; CV_SetValue(&cv_usejoystick4, choice); // Just in case last-minute changes were made to cv_usejoystick.value, // update the string too // But don't do this if we're intentionally setting higher than numjoys if (choice <= numjoys) { CV_SetValue(&cv_usejoystick4, cv_usejoystick4.value); // reset this so the comparison is valid if (oldchoice > numjoys) oldchoice = cv_usejoystick4.value; if (oldchoice != choice) { if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device CV_SetValue(&cv_usejoystick4, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); if (oldstringchoice == (atoi(cv_usejoystick4.string) > numjoys ? atoi(cv_usejoystick4.string) : cv_usejoystick4.value)) M_StartMessage("This joystick is used by another\n" "player. Reset the joystick\n" "for that player first.\n\n" "(Press a key)\n", NULL, MM_NOTHING); } } } else if (setupcontrolplayer == 3) { oldchoice = oldstringchoice = atoi(cv_usejoystick3.string) > numjoys ? atoi(cv_usejoystick3.string) : cv_usejoystick3.value; CV_SetValue(&cv_usejoystick3, choice); // Just in case last-minute changes were made to cv_usejoystick.value, // update the string too // But don't do this if we're intentionally setting higher than numjoys if (choice <= numjoys) { CV_SetValue(&cv_usejoystick3, cv_usejoystick3.value); // reset this so the comparison is valid if (oldchoice > numjoys) oldchoice = cv_usejoystick3.value; if (oldchoice != choice) { if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device CV_SetValue(&cv_usejoystick3, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); if (oldstringchoice == (atoi(cv_usejoystick3.string) > numjoys ? atoi(cv_usejoystick3.string) : cv_usejoystick3.value)) M_StartMessage("This joystick is used by another\n" "player. Reset the joystick\n" "for that player first.\n\n" "(Press a key)\n", NULL, MM_NOTHING); } } } else if (setupcontrolplayer == 2) { oldchoice = oldstringchoice = atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value; CV_SetValue(&cv_usejoystick2, choice); // Just in case last-minute changes were made to cv_usejoystick.value, // update the string too // But don't do this if we're intentionally setting higher than numjoys if (choice <= numjoys) { CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); // reset this so the comparison is valid if (oldchoice > numjoys) oldchoice = cv_usejoystick2.value; if (oldchoice != choice) { if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device CV_SetValue(&cv_usejoystick2, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); if (oldstringchoice == (atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value)) M_StartMessage("This joystick is used by another\n" "player. Reset the joystick\n" "for that player first.\n\n" "(Press a key)\n", NULL, MM_NOTHING); } } } else if (setupcontrolplayer == 1) { oldchoice = oldstringchoice = atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value; CV_SetValue(&cv_usejoystick, choice); // Just in case last-minute changes were made to cv_usejoystick.value, // update the string too // But don't do this if we're intentionally setting higher than numjoys if (choice <= numjoys) { CV_SetValue(&cv_usejoystick, cv_usejoystick.value); // reset this so the comparison is valid if (oldchoice > numjoys) oldchoice = cv_usejoystick.value; if (oldchoice != choice) { if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device CV_SetValue(&cv_usejoystick, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); if (oldstringchoice == (atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value)) M_StartMessage("This joystick is used by another\n" "player. Reset the joystick\n" "for that player first.\n\n" "(Press a key)\n", NULL, MM_NOTHING); } } } #else if (setupcontrolplayer == 4) CV_SetValue(&cv_usejoystick4, choice); else if (setupcontrolplayer == 3) CV_SetValue(&cv_usejoystick3, choice); else if (setupcontrolplayer == 2) CV_SetValue(&cv_usejoystick2, choice); else if (setupcontrolplayer == 1) CV_SetValue(&cv_usejoystick, choice); #endif } // ============= // CONTROLS MENU // ============= static void M_Setup1PControlsMenu(INT32 choice) { (void)choice; setupcontrolplayer = 1; setupcontrols = gamecontrol; // was called from main Options (for console player, then) currentMenu->lastOn = itemOn; // Set proper gamepad options OP_AllControlsMenu[0].itemaction = &OP_Joystick1Def; // Unhide P1-only controls OP_AllControlsMenu[15].status = IT_CONTROL; // Chat //OP_AllControlsMenu[16].status = IT_CONTROL; // Team-chat OP_AllControlsMenu[16].status = IT_CONTROL; // Rankings //OP_AllControlsMenu[17].status = IT_CONTROL; // Viewpoint // 18 is Reset Camera, 19 is Toggle Chasecam OP_AllControlsMenu[20].status = IT_CONTROL; // Pause OP_AllControlsMenu[21].status = IT_CONTROL; // Screenshot OP_AllControlsMenu[22].status = IT_CONTROL; // GIF OP_AllControlsMenu[23].status = IT_CONTROL; // System Menu OP_AllControlsMenu[24].status = IT_CONTROL; // Console /*OP_AllControlsMenu[25].status = IT_HEADER; // Spectator Controls header OP_AllControlsMenu[26].status = IT_SPACE; // Spectator Controls space OP_AllControlsMenu[27].status = IT_CONTROL; // Spectate OP_AllControlsMenu[28].status = IT_CONTROL; // Look Up OP_AllControlsMenu[29].status = IT_CONTROL; // Look Down OP_AllControlsMenu[30].status = IT_CONTROL; // Center View */ M_SetupNextMenu(&OP_AllControlsDef); } static void M_Setup2PControlsMenu(INT32 choice) { (void)choice; setupcontrolplayer = 2; setupcontrols = gamecontrolbis; currentMenu->lastOn = itemOn; // Set proper gamepad options OP_AllControlsMenu[0].itemaction = &OP_Joystick2Def; // Hide P1-only controls OP_AllControlsMenu[15].status = IT_GRAYEDOUT2; // Chat //OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Team-chat OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Rankings //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint // 18 is Reset Camera, 19 is Toggle Chasecam OP_AllControlsMenu[20].status = IT_GRAYEDOUT2; // Pause OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Screenshot OP_AllControlsMenu[22].status = IT_GRAYEDOUT2; // GIF OP_AllControlsMenu[23].status = IT_GRAYEDOUT2; // System Menu OP_AllControlsMenu[24].status = IT_GRAYEDOUT2; // Console /*OP_AllControlsMenu[25].status = IT_GRAYEDOUT2; // Spectator Controls header OP_AllControlsMenu[26].status = IT_GRAYEDOUT2; // Spectator Controls space OP_AllControlsMenu[27].status = IT_GRAYEDOUT2; // Spectate OP_AllControlsMenu[28].status = IT_GRAYEDOUT2; // Look Up OP_AllControlsMenu[29].status = IT_GRAYEDOUT2; // Look Down OP_AllControlsMenu[30].status = IT_GRAYEDOUT2; // Center View */ M_SetupNextMenu(&OP_AllControlsDef); } static void M_Setup3PControlsMenu(INT32 choice) { (void)choice; setupcontrolplayer = 3; setupcontrols = gamecontrol3; currentMenu->lastOn = itemOn; // Set proper gamepad options OP_AllControlsMenu[0].itemaction = &OP_Joystick3Def; // Hide P1-only controls OP_AllControlsMenu[15].status = IT_GRAYEDOUT2; // Chat //OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Team-chat OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Rankings //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint // 18 is Reset Camera, 19 is Toggle Chasecam OP_AllControlsMenu[20].status = IT_GRAYEDOUT2; // Pause OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Screenshot OP_AllControlsMenu[22].status = IT_GRAYEDOUT2; // GIF OP_AllControlsMenu[23].status = IT_GRAYEDOUT2; // System Menu OP_AllControlsMenu[24].status = IT_GRAYEDOUT2; // Console /*OP_AllControlsMenu[25].status = IT_GRAYEDOUT2; // Spectator Controls header OP_AllControlsMenu[26].status = IT_GRAYEDOUT2; // Spectator Controls space OP_AllControlsMenu[27].status = IT_GRAYEDOUT2; // Spectate OP_AllControlsMenu[28].status = IT_GRAYEDOUT2; // Look Up OP_AllControlsMenu[29].status = IT_GRAYEDOUT2; // Look Down OP_AllControlsMenu[30].status = IT_GRAYEDOUT2; // Center View */ M_SetupNextMenu(&OP_AllControlsDef); } static void M_Setup4PControlsMenu(INT32 choice) { (void)choice; setupcontrolplayer = 4; setupcontrols = gamecontrol4; currentMenu->lastOn = itemOn; // Set proper gamepad options OP_AllControlsMenu[0].itemaction = &OP_Joystick4Def; // Hide P1-only controls OP_AllControlsMenu[15].status = IT_GRAYEDOUT2; // Chat //OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Team-chat OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Rankings //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint // 18 is Reset Camera, 19 is Toggle Chasecam OP_AllControlsMenu[20].status = IT_GRAYEDOUT2; // Pause OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Screenshot OP_AllControlsMenu[22].status = IT_GRAYEDOUT2; // GIF OP_AllControlsMenu[23].status = IT_GRAYEDOUT2; // System Menu OP_AllControlsMenu[24].status = IT_GRAYEDOUT2; // Console /*OP_AllControlsMenu[25].status = IT_GRAYEDOUT2; // Spectator Controls header OP_AllControlsMenu[26].status = IT_GRAYEDOUT2; // Spectator Controls space OP_AllControlsMenu[27].status = IT_GRAYEDOUT2; // Spectate OP_AllControlsMenu[28].status = IT_GRAYEDOUT2; // Look Up OP_AllControlsMenu[29].status = IT_GRAYEDOUT2; // Look Down OP_AllControlsMenu[30].status = IT_GRAYEDOUT2; // Center View */ M_SetupNextMenu(&OP_AllControlsDef); } #define controlheight 18 // Draws the Customise Controls menu static void M_DrawControl(void) { char tmp[50]; INT32 x, y, i, max, cursory = 0, iter; INT32 keys[2]; x = currentMenu->x; y = currentMenu->y; /*i = itemOn - (controlheight/2); if (i < 0) i = 0; */ iter = (controlheight/2); for (i = itemOn; ((iter || currentMenu->menuitems[i].status == IT_GRAYEDOUT2) && i > 0); i--) { if (currentMenu->menuitems[i].status != IT_GRAYEDOUT2) iter--; } if (currentMenu->menuitems[i].status == IT_GRAYEDOUT2) i--; iter += (controlheight/2); for (max = itemOn; (iter && max < currentMenu->numitems); max++) { if (currentMenu->menuitems[max].status != IT_GRAYEDOUT2) iter--; } if (iter) { iter += (controlheight/2); for (i = itemOn; ((iter || currentMenu->menuitems[i].status == IT_GRAYEDOUT2) && i > 0); i--) { if (currentMenu->menuitems[i].status != IT_GRAYEDOUT2) iter--; } } /*max = i + controlheight; if (max > currentMenu->numitems) { max = currentMenu->numitems; if (max < controlheight) i = 0; else i = max - controlheight; }*/ // draw title (or big pic) M_DrawMenuTitle(); M_CentreText(28, (setupcontrolplayer > 1 ? va("\x86""Set controls for ""\x82""Player %d", setupcontrolplayer) : "\x86""Press ""\x82""ENTER""\x86"" to change, ""\x82""BACKSPACE""\x86"" to clear")); if (i) V_DrawCharacter(currentMenu->x - 16, y-(skullAnimCounter/5), '\x1A' | highlightflags, false); // up arrow if (max != currentMenu->numitems) V_DrawCharacter(currentMenu->x - 16, y+(SMALLLINEHEIGHT*(controlheight-1))+(skullAnimCounter/5) + (skullAnimCounter/5), '\x1B' | highlightflags, false); // down arrow for (; i < max; i++) { if (currentMenu->menuitems[i].status == IT_GRAYEDOUT2) continue; if (i == itemOn) cursory = y; if (currentMenu->menuitems[i].status == IT_CONTROL) { V_DrawString(x, y, ((i == itemOn) ? highlightflags : 0), currentMenu->menuitems[i].text); keys[0] = setupcontrols[currentMenu->menuitems[i].alphaKey][0]; keys[1] = setupcontrols[currentMenu->menuitems[i].alphaKey][1]; tmp[0] ='\0'; if (keys[0] == KEY_NULL && keys[1] == KEY_NULL) { strcpy(tmp, "---"); } else { if (keys[0] != KEY_NULL) strcat (tmp, G_KeynumToString (keys[0])); if (keys[0] != KEY_NULL && keys[1] != KEY_NULL) strcat(tmp,", "); if (keys[1] != KEY_NULL) strcat (tmp, G_KeynumToString (keys[1])); } V_DrawRightAlignedString(BASEVIDWIDTH-currentMenu->x, y, highlightflags, tmp); } /*else if (currentMenu->menuitems[i].status == IT_GRAYEDOUT2) V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);*/ else if ((currentMenu->menuitems[i].status == IT_HEADER) && (i != max-1)) V_DrawString(19, y+6, highlightflags, currentMenu->menuitems[i].text); else if (currentMenu->menuitems[i].status & IT_STRING) V_DrawString(x, y, ((i == itemOn) ? highlightflags : 0), currentMenu->menuitems[i].text); y += SMALLLINEHEIGHT; } V_DrawScaledPatch(currentMenu->x - 20, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); } #undef controlheight static INT32 controltochange; static char controltochangetext[33]; static void M_ChangecontrolResponse(event_t *ev) { INT32 control; INT32 found; INT32 ch = ev->data1; // ESCAPE cancels; dummy out PAUSE if (ch != KEY_ESCAPE && ch != KEY_PAUSE) { switch (ev->type) { // ignore mouse/joy movements, just get buttons case ev_mouse: case ev_mouse2: case ev_joystick: case ev_joystick2: case ev_joystick3: case ev_joystick4: ch = KEY_NULL; // no key break; // keypad arrows are converted for the menu in cursor arrows // so use the event instead of ch case ev_keydown: ch = ev->data1; break; default: break; } control = controltochange; // check if we already entered this key found = -1; if (setupcontrols[control][0] ==ch) found = 0; else if (setupcontrols[control][1] ==ch) found = 1; if (found >= 0) { // replace mouse and joy clicks by double clicks if (ch >= KEY_MOUSE1 && ch <= KEY_MOUSE1+MOUSEBUTTONS) setupcontrols[control][found] = ch-KEY_MOUSE1+KEY_DBLMOUSE1; else if (ch >= KEY_JOY1 && ch <= KEY_JOY1+JOYBUTTONS) setupcontrols[control][found] = ch-KEY_JOY1+KEY_DBLJOY1; else if (ch >= KEY_2MOUSE1 && ch <= KEY_2MOUSE1+MOUSEBUTTONS) setupcontrols[control][found] = ch-KEY_2MOUSE1+KEY_DBL2MOUSE1; else if (ch >= KEY_2JOY1 && ch <= KEY_2JOY1+JOYBUTTONS) setupcontrols[control][found] = ch-KEY_2JOY1+KEY_DBL2JOY1; else if (ch >= KEY_3JOY1 && ch <= KEY_3JOY1+JOYBUTTONS) setupcontrols[control][found] = ch-KEY_3JOY1+KEY_DBL3JOY1; else if (ch >= KEY_4JOY1 && ch <= KEY_4JOY1+JOYBUTTONS) setupcontrols[control][found] = ch-KEY_4JOY1+KEY_DBL4JOY1; } else { // check if change key1 or key2, or replace the two by the new found = 0; if (setupcontrols[control][0] == KEY_NULL) found++; if (setupcontrols[control][1] == KEY_NULL) found++; if (found == 2) { found = 0; setupcontrols[control][1] = KEY_NULL; //replace key 1,clear key2 } (void)G_CheckDoubleUsage(ch, true); setupcontrols[control][found] = ch; } S_StartSound(NULL, sfx_s221); } else if (ch == KEY_PAUSE) { // This buffer assumes a 125-character message plus a 32-character control name (per controltochangetext buffer size) static char tmp[158]; menu_t *prev = currentMenu->prevMenu; if (controltochange == gc_pause) sprintf(tmp, M_GetText("The \x82Pause Key \x80is enabled, but \nyou may select another key. \n\nHit another key for\n%s\nESC for Cancel"), controltochangetext); else sprintf(tmp, M_GetText("The \x82Pause Key \x80is enabled, but \nit is not configurable. \n\nHit another key for\n%s\nESC for Cancel"), controltochangetext); M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER); currentMenu->prevMenu = prev; S_StartSound(NULL, sfx_s3k42); return; } else S_StartSound(NULL, sfx_s224); M_StopMessage(0); } static void M_ChangeControl(INT32 choice) { // This buffer assumes a 35-character message (per below) plus a max control name limit of 32 chars (per controltochangetext) // If you change the below message, then change the size of this buffer! static char tmp[68]; controltochange = currentMenu->menuitems[choice].alphaKey; sprintf(tmp, M_GetText("Hit the new key for\n%s\nESC for Cancel"), currentMenu->menuitems[choice].text); strlcpy(controltochangetext, currentMenu->menuitems[choice].text, 33); M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER); } static void M_ResetControlsResponse(INT32 ch) { INT32 i; if (ch != 'y' && ch != KEY_ENTER) return; // clear all controls for (i = 0; i < num_gamecontrols; i++) { switch (setupcontrolplayer) { case 4: G_ClearControlKeys(gamecontrol4, i); break; case 3: G_ClearControlKeys(gamecontrol3, i); break; case 2: G_ClearControlKeys(gamecontrolbis, i); break; case 1: default: G_ClearControlKeys(gamecontrol, i); break; } } // Setup original defaults G_Controldefault(setupcontrolplayer); // Setup gamepad option defaults (yucky) switch (setupcontrolplayer) { case 4: CV_StealthSet(&cv_usejoystick4, cv_usejoystick4.defaultvalue); CV_StealthSet(&cv_turnaxis4, cv_turnaxis4.defaultvalue); CV_StealthSet(&cv_moveaxis4, cv_moveaxis4.defaultvalue); CV_StealthSet(&cv_brakeaxis4, cv_brakeaxis4.defaultvalue); CV_StealthSet(&cv_aimaxis4, cv_aimaxis4.defaultvalue); CV_StealthSet(&cv_lookaxis4, cv_lookaxis4.defaultvalue); CV_StealthSet(&cv_fireaxis4, cv_fireaxis4.defaultvalue); CV_StealthSet(&cv_driftaxis4, cv_driftaxis4.defaultvalue); break; case 3: CV_StealthSet(&cv_usejoystick3, cv_usejoystick3.defaultvalue); CV_StealthSet(&cv_turnaxis3, cv_turnaxis3.defaultvalue); CV_StealthSet(&cv_moveaxis3, cv_moveaxis3.defaultvalue); CV_StealthSet(&cv_brakeaxis3, cv_brakeaxis3.defaultvalue); CV_StealthSet(&cv_aimaxis3, cv_aimaxis3.defaultvalue); CV_StealthSet(&cv_lookaxis3, cv_lookaxis3.defaultvalue); CV_StealthSet(&cv_fireaxis3, cv_fireaxis3.defaultvalue); CV_StealthSet(&cv_driftaxis3, cv_driftaxis3.defaultvalue); break; case 2: CV_StealthSet(&cv_usejoystick2, cv_usejoystick2.defaultvalue); CV_StealthSet(&cv_turnaxis2, cv_turnaxis2.defaultvalue); CV_StealthSet(&cv_moveaxis2, cv_moveaxis2.defaultvalue); CV_StealthSet(&cv_brakeaxis2, cv_brakeaxis2.defaultvalue); CV_StealthSet(&cv_aimaxis2, cv_aimaxis2.defaultvalue); CV_StealthSet(&cv_lookaxis2, cv_lookaxis2.defaultvalue); CV_StealthSet(&cv_fireaxis2, cv_fireaxis2.defaultvalue); CV_StealthSet(&cv_driftaxis2, cv_driftaxis2.defaultvalue); break; case 1: default: CV_StealthSet(&cv_usejoystick, cv_usejoystick.defaultvalue); CV_StealthSet(&cv_turnaxis, cv_turnaxis.defaultvalue); CV_StealthSet(&cv_moveaxis, cv_moveaxis.defaultvalue); CV_StealthSet(&cv_brakeaxis, cv_brakeaxis.defaultvalue); CV_StealthSet(&cv_aimaxis, cv_aimaxis.defaultvalue); CV_StealthSet(&cv_lookaxis, cv_lookaxis.defaultvalue); CV_StealthSet(&cv_fireaxis, cv_fireaxis.defaultvalue); CV_StealthSet(&cv_driftaxis, cv_driftaxis.defaultvalue); break; } S_StartSound(NULL, sfx_s224); } static void M_ResetControls(INT32 choice) { (void)choice; M_StartMessage(va(M_GetText("Reset Player %d's controls to defaults?\n\n(Press 'Y' to confirm)\n"), setupcontrolplayer), M_ResetControlsResponse, MM_YESNO); } // ===== // SOUND // ===== /*static void M_RestartAudio(void) { COM_ImmedExecute("restartaudio"); }*/ // =============== // VIDEO MODE MENU // =============== //added : 30-01-98: #define MAXCOLUMNMODES 12 //max modes displayed in one column #define MAXMODEDESCS (MAXCOLUMNMODES*3) static modedesc_t modedescs[MAXMODEDESCS]; static void M_VideoModeMenu(INT32 choice) { INT32 i, j, vdup, nummodes, width, height; const char *desc; (void)choice; memset(modedescs, 0, sizeof(modedescs)); #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) VID_PrepareModeList(); // FIXME: hack #endif vidm_nummodes = 0; vidm_selected = 0; nummodes = VID_NumModes(); #ifdef _WINDOWS // clean that later: skip windowed mode 0, video modes menu only shows FULL SCREEN modes if (nummodes <= NUMSPECIALMODES) i = 0; // unless we have nothing else i = NUMSPECIALMODES; #else // DOS does not skip mode 0, because mode 0 is ALWAYS present i = 0; #endif for (; i < nummodes && vidm_nummodes < MAXMODEDESCS; i++) { desc = VID_GetModeName(i); if (desc) { vdup = 0; // when a resolution exists both under VGA and VESA, keep the // VESA mode, which is always a higher modenum for (j = 0; j < vidm_nummodes; j++) { if (!strcmp(modedescs[j].desc, desc)) { // mode(0): 320x200 is always standard VGA, not vesa if (modedescs[j].modenum) { modedescs[j].modenum = i; vdup = 1; if (i == vid.modenum) vidm_selected = j; } else vdup = 1; break; } } if (!vdup) { modedescs[vidm_nummodes].modenum = i; modedescs[vidm_nummodes].desc = desc; if (i == vid.modenum) vidm_selected = vidm_nummodes; // Pull out the width and height sscanf(desc, "%u%*c%u", &width, &height); // Show multiples of 320x200 as green. if (SCR_IsAspectCorrect(width, height)) modedescs[vidm_nummodes].goodratio = 1; vidm_nummodes++; } } } vidm_column_size = (vidm_nummodes+2) / 3; M_SetupNextMenu(&OP_VideoModeDef); } static void M_DrawVideoMenu(void) { M_DrawGenericMenu(); V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + OP_VideoOptionsMenu[0].alphaKey, (SCR_IsAspectCorrect(vid.width, vid.height) ? recommendedflags : highlightflags), va("%dx%d", vid.width, vid.height)); } static void M_DrawHUDOptions(void) { const char *str0 = ")"; const char *str1 = " Warning highlight"; const char *str2 = ","; const char *str3 = "Good highlight"; INT32 x = BASEVIDWIDTH - currentMenu->x + 2, y = currentMenu->y + 105; INT32 w0 = V_StringWidth(str0, 0), w1 = V_StringWidth(str1, 0), w2 = V_StringWidth(str2, 0), w3 = V_StringWidth(str3, 0); M_DrawGenericMenu(); x -= w0; V_DrawString(x, y, highlightflags, str0); x -= w1; V_DrawString(x, y, warningflags, str1); x -= w2; V_DrawString(x, y, highlightflags, str2); x -= w3; V_DrawString(x, y, recommendedflags, str3); V_DrawRightAlignedString(x, y, highlightflags, "("); } // Draw the video modes list, a-la-Quake static void M_DrawVideoMode(void) { INT32 i, j, row, col; // draw title M_DrawMenuTitle(); V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y, highlightflags, "Choose mode, reselect to change default"); row = 41; col = OP_VideoModeDef.y + 14; for (i = 0; i < vidm_nummodes; i++) { if (i == vidm_selected) V_DrawString(row, col, highlightflags, modedescs[i].desc); // Show multiples of 320x200 as green. else V_DrawString(row, col, (modedescs[i].goodratio) ? recommendedflags : 0, modedescs[i].desc); col += 8; if ((i % vidm_column_size) == (vidm_column_size-1)) { row += 7*13; col = OP_VideoModeDef.y + 14; } } if (vidm_testingmode > 0) { INT32 testtime = (vidm_testingmode/TICRATE) + 1; M_CentreText(OP_VideoModeDef.y + 116, va("Previewing mode %c%dx%d", (SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80, vid.width, vid.height)); M_CentreText(OP_VideoModeDef.y + 138, "Press ENTER again to keep this mode"); M_CentreText(OP_VideoModeDef.y + 150, va("Wait %d second%s", testtime, (testtime > 1) ? "s" : "")); M_CentreText(OP_VideoModeDef.y + 158, "or press ESC to return"); } else { M_CentreText(OP_VideoModeDef.y + 116, va("Current mode is %c%dx%d", (SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80, vid.width, vid.height)); M_CentreText(OP_VideoModeDef.y + 124, va("Default mode is %c%dx%d", (SCR_IsAspectCorrect(cv_scr_width.value, cv_scr_height.value)) ? 0x83 : 0x80, cv_scr_width.value, cv_scr_height.value)); V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 138, recommendedflags, "Marked modes are recommended."); V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 146, highlightflags, "Other modes may have visual errors."); V_DrawCenteredString(BASEVIDWIDTH/2, OP_VideoModeDef.y + 158, highlightflags, "Larger modes may have performance issues."); } // Draw the cursor for the VidMode menu i = 41 - 10 + ((vidm_selected / vidm_column_size)*7*13); j = OP_VideoModeDef.y + 14 + ((vidm_selected % vidm_column_size)*8); V_DrawScaledPatch(i - 8, j, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); } // special menuitem key handler for video mode list static void M_HandleVideoMode(INT32 ch) { if (vidm_testingmode > 0) switch (ch) { // change back to the previous mode quickly case KEY_ESCAPE: setmodeneeded = vidm_previousmode + 1; vidm_testingmode = 0; break; case KEY_ENTER: S_StartSound(NULL, sfx_menu1); vidm_testingmode = 0; // stop testing } else switch (ch) { case KEY_DOWNARROW: S_StartSound(NULL, sfx_menu1); if (++vidm_selected >= vidm_nummodes) vidm_selected = 0; break; case KEY_UPARROW: S_StartSound(NULL, sfx_menu1); if (--vidm_selected < 0) vidm_selected = vidm_nummodes - 1; break; case KEY_LEFTARROW: S_StartSound(NULL, sfx_menu1); vidm_selected -= vidm_column_size; if (vidm_selected < 0) vidm_selected = (vidm_column_size*3) + vidm_selected; if (vidm_selected >= vidm_nummodes) vidm_selected = vidm_nummodes - 1; break; case KEY_RIGHTARROW: S_StartSound(NULL, sfx_menu1); vidm_selected += vidm_column_size; if (vidm_selected >= (vidm_column_size*3)) vidm_selected %= vidm_column_size; if (vidm_selected >= vidm_nummodes) vidm_selected = vidm_nummodes - 1; break; case KEY_ENTER: S_StartSound(NULL, sfx_menu1); if (vid.modenum == modedescs[vidm_selected].modenum) SCR_SetDefaultMode(); else { vidm_testingmode = 15*TICRATE; vidm_previousmode = vid.modenum; if (!setmodeneeded) // in case the previous setmode was not finished setmodeneeded = modedescs[vidm_selected].modenum + 1; } break; case KEY_ESCAPE: // this one same as M_Responder if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); break; default: break; } } // =============== // Monitor Toggles // =============== static consvar_t *kartitemcvs[NUMKARTRESULTS-1] = { &cv_sneaker, &cv_rocketsneaker, &cv_invincibility, &cv_banana, &cv_eggmanmonitor, &cv_orbinaut, &cv_jawz, &cv_mine, &cv_ballhog, &cv_selfpropelledbomb, &cv_grow, &cv_shrink, &cv_thundershield, &cv_hyudoro, &cv_pogospring, &cv_kitchensink, &cv_triplesneaker, &cv_triplebanana, &cv_decabanana, &cv_tripleorbinaut, &cv_quadorbinaut, &cv_dualjawz }; static tic_t shitsfree = 0; static void M_DrawMonitorToggles(void) { const INT32 edges = 4; const INT32 height = 4; const INT32 spacing = 35; const INT32 column = itemOn/height; //const INT32 row = itemOn%height; INT32 leftdraw, rightdraw, totaldraw; INT32 x = currentMenu->x, y = currentMenu->y+(spacing/4); INT32 onx = 0, ony = 0; consvar_t *cv; INT32 i, translucent, drawnum; M_DrawMenuTitle(); // Find the available space around column leftdraw = rightdraw = column; totaldraw = 0; for (i = 0; (totaldraw < edges*2 && i < edges*4); i++) { if (rightdraw+1 < (currentMenu->numitems/height)+1) { rightdraw++; totaldraw++; } if (leftdraw-1 >= 0) { leftdraw--; totaldraw++; } } for (i = leftdraw; i <= rightdraw; i++) { INT32 j; for (j = 0; j < height; j++) { const INT32 thisitem = (i*height)+j; if (thisitem >= currentMenu->numitems) continue; if (thisitem == itemOn) { onx = x; ony = y; y += spacing; continue; } #ifdef ITEMTOGGLEBOTTOMRIGHT if (currentMenu->menuitems[thisitem].alphaKey == 255) { V_DrawScaledPatch(x, y, V_TRANSLUCENT, W_CachePatchName("K_ISBG", PU_CACHE)); continue; } #endif if (currentMenu->menuitems[thisitem].alphaKey == 0) { V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBG", PU_CACHE)); V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISTOGL", PU_CACHE)); continue; } cv = kartitemcvs[currentMenu->menuitems[thisitem].alphaKey-1]; translucent = (cv->value ? 0 : V_TRANSLUCENT); switch (currentMenu->menuitems[thisitem].alphaKey) { case KRITEM_DUALJAWZ: drawnum = 2; break; case KRITEM_TRIPLESNEAKER: case KRITEM_TRIPLEBANANA: case KRITEM_TRIPLEORBINAUT: drawnum = 3; break; case KRITEM_QUADORBINAUT: drawnum = 4; break; case KRITEM_TENFOLDBANANA: drawnum = 10; break; default: drawnum = 0; break; } if (cv->value) V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBG", PU_CACHE)); else V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBGD", PU_CACHE)); if (drawnum != 0) { V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISMUL", PU_CACHE)); V_DrawScaledPatch(x, y, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[thisitem].alphaKey, true), PU_CACHE)); V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|translucent, va("x%d", drawnum)); } else V_DrawScaledPatch(x, y, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[thisitem].alphaKey, true), PU_CACHE)); y += spacing; } x += spacing; y = currentMenu->y+(spacing/4); } { #ifdef ITEMTOGGLEBOTTOMRIGHT if (currentMenu->menuitems[itemOn].alphaKey == 255) { V_DrawScaledPatch(onx-1, ony-2, V_TRANSLUCENT, W_CachePatchName("K_ITBG", PU_CACHE)); if (shitsfree) { INT32 trans = V_TRANSLUCENT; if (shitsfree-1 > TICRATE-5) trans = ((10-TICRATE)+shitsfree-1)<menuitems[itemOn].alphaKey == 0) { V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITBG", PU_CACHE)); V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITTOGL", PU_CACHE)); } else { cv = kartitemcvs[currentMenu->menuitems[itemOn].alphaKey-1]; translucent = (cv->value ? 0 : V_TRANSLUCENT); switch (currentMenu->menuitems[itemOn].alphaKey) { case KRITEM_DUALJAWZ: drawnum = 2; break; case KRITEM_TRIPLESNEAKER: case KRITEM_TRIPLEBANANA: drawnum = 3; break; case KRITEM_TENFOLDBANANA: drawnum = 10; break; default: drawnum = 0; break; } if (cv->value) V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITBG", PU_CACHE)); else V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITBGD", PU_CACHE)); if (drawnum != 0) { V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITMUL", PU_CACHE)); V_DrawScaledPatch(onx-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].alphaKey, false), PU_CACHE)); V_DrawScaledPatch(onx+27, ony+39, translucent, W_CachePatchName("K_ITX", PU_CACHE)); V_DrawKartString(onx+37, ony+34, translucent, va("%d", drawnum)); } else V_DrawScaledPatch(onx-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].alphaKey, false), PU_CACHE)); } } if (shitsfree) shitsfree--; V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y, highlightflags, va("* %s *", currentMenu->menuitems[itemOn].text)); } static void M_HandleMonitorToggles(INT32 choice) { const INT32 width = 6, height = 4; INT32 column = itemOn/height, row = itemOn%height; INT16 next; UINT8 i; boolean exitmenu = false; switch (choice) { case KEY_RIGHTARROW: S_StartSound(NULL, sfx_menu1); column++; if (((column*height)+row) >= currentMenu->numitems) column = 0; next = min(((column*height)+row), currentMenu->numitems-1); itemOn = next; break; case KEY_LEFTARROW: S_StartSound(NULL, sfx_menu1); column--; if (column < 0) column = width-1; if (((column*height)+row) >= currentMenu->numitems) column--; next = max(((column*height)+row), 0); if (next >= currentMenu->numitems) next = currentMenu->numitems-1; itemOn = next; break; case KEY_DOWNARROW: S_StartSound(NULL, sfx_menu1); row = (row+1) % height; if (((column*height)+row) >= currentMenu->numitems) row = 0; next = min(((column*height)+row), currentMenu->numitems-1); itemOn = next; break; case KEY_UPARROW: S_StartSound(NULL, sfx_menu1); row = (row-1) % height; if (row < 0) row = height-1; if (((column*height)+row) >= currentMenu->numitems) row--; next = max(((column*height)+row), 0); if (next >= currentMenu->numitems) next = currentMenu->numitems-1; itemOn = next; break; case KEY_ENTER: #ifdef ITEMTOGGLEBOTTOMRIGHT if (currentMenu->menuitems[itemOn].alphaKey == 255) { //S_StartSound(NULL, sfx_s26d); if (!shitsfree) { shitsfree = TICRATE; S_StartSound(NULL, sfx_itfree); } } else #endif if (currentMenu->menuitems[itemOn].alphaKey == 0) { INT32 v = cv_sneaker.value; S_StartSound(NULL, sfx_s1b4); for (i = 0; i < NUMKARTRESULTS-1; i++) { if (kartitemcvs[i]->value == v) CV_AddValue(kartitemcvs[i], 1); } } else { S_StartSound(NULL, sfx_s1ba); CV_AddValue(kartitemcvs[currentMenu->menuitems[itemOn].alphaKey-1], 1); } break; case KEY_ESCAPE: exitmenu = true; break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } // ========= // Quit Game // ========= static INT32 quitsounds[] = { // holy shit we're changing things up! // srb2kart: you ain't seen nothing yet sfx_kc2e, sfx_kc2f, sfx_cdfm01, sfx_ddash, sfx_s3ka2, sfx_s3k49, sfx_slip, sfx_tossed, sfx_s3k7b, sfx_itrolf, sfx_itrole, sfx_cdpcm9, sfx_s3k4e, sfx_s259, sfx_3db06, sfx_s3k3a, sfx_peel, sfx_cdfm28, sfx_s3k96, sfx_s3kc0s, sfx_cdfm39, sfx_hogbom, sfx_kc5a, sfx_kc46, sfx_s3k92, sfx_s3k42, sfx_kpogos, sfx_screec }; void M_QuitResponse(INT32 ch) { tic_t ptime; INT32 mrand; if (ch != 'y' && ch != KEY_ENTER) return; if (!(netgame || cv_debug)) { mrand = M_RandomKey(sizeof(quitsounds)/sizeof(INT32)); if (quitsounds[mrand]) S_StartSound(NULL, quitsounds[mrand]); //added : 12-02-98: do that instead of I_WaitVbl which does not work ptime = I_GetTime() + NEWTICRATE*2; // Shortened the quit time, used to be 2 seconds Tails 03-26-2001 while (ptime > I_GetTime()) { V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); V_DrawSmallScaledPatch(0, 0, 0, W_CachePatchName("GAMEQUIT", PU_CACHE)); // Demo 3 Quit Screen Tails 06-16-2001 I_FinishUpdate(); // Update the screen with the image Tails 06-19-2001 I_Sleep(); } } I_Quit(); } static void M_QuitSRB2(INT32 choice) { // We pick index 0 which is language sensitive, or one at random, // between 1 and maximum number. (void)choice; M_StartMessage(quitmsg[M_RandomKey(NUM_QUITMESSAGES)], M_QuitResponse, MM_YESNO); } #ifdef HWRENDER // ===================================================================== // OpenGL specific options // ===================================================================== #define FOG_COLOR_ITEM 1 // =================== // M_OGL_DrawFogMenu() // =================== static void M_OGL_DrawFogMenu(void) { INT32 mx, my; mx = currentMenu->x; my = currentMenu->y; M_DrawGenericMenu(); // use generic drawer for cursor, items and title V_DrawString(BASEVIDWIDTH - mx - V_StringWidth(cv_grfogcolor.string, 0), my + currentMenu->menuitems[FOG_COLOR_ITEM].alphaKey, highlightflags, cv_grfogcolor.string); // blink cursor on FOG_COLOR_ITEM if selected if (itemOn == FOG_COLOR_ITEM && skullAnimCounter < 4) V_DrawCharacter(BASEVIDWIDTH - mx, my + currentMenu->menuitems[FOG_COLOR_ITEM].alphaKey, '_' | 0x80,false); } // ===================== // M_OGL_DrawColorMenu() // ===================== static void M_OGL_DrawColorMenu(void) { INT32 mx, my; mx = currentMenu->x; my = currentMenu->y; M_DrawGenericMenu(); // use generic drawer for cursor, items and title V_DrawString(mx, my + currentMenu->menuitems[0].alphaKey - 10, highlightflags, "Gamma correction"); } //=================== // M_HandleFogColor() //=================== static void M_HandleFogColor(INT32 choice) { size_t i, l; char temp[8]; boolean exitmenu = false; // exit to previous menu and send name change switch (choice) { case KEY_DOWNARROW: S_StartSound(NULL, sfx_menu1); itemOn++; break; case KEY_UPARROW: S_StartSound(NULL, sfx_menu1); itemOn--; break; case KEY_ESCAPE: exitmenu = true; break; case KEY_BACKSPACE: S_StartSound(NULL, sfx_menu1); strcpy(temp, cv_grfogcolor.string); strcpy(cv_grfogcolor.zstring, "000000"); l = strlen(temp)-1; for (i = 0; i < l; i++) cv_grfogcolor.zstring[i + 6 - l] = temp[i]; break; default: if ((choice >= '0' && choice <= '9') || (choice >= 'a' && choice <= 'f') || (choice >= 'A' && choice <= 'F')) { S_StartSound(NULL, sfx_menu1); strcpy(temp, cv_grfogcolor.string); strcpy(cv_grfogcolor.zstring, "000000"); l = strlen(temp); for (i = 0; i < l; i++) cv_grfogcolor.zstring[5 - i] = temp[l - i]; cv_grfogcolor.zstring[5] = (char)choice; } break; } if (exitmenu) { if (currentMenu->prevMenu) M_SetupNextMenu(currentMenu->prevMenu); else M_ClearMenus(true); } } #endif